Direct3D 9Ex の機能強化

このトピックでは、Direct3D 9Ex およびデスクトップ ウィンドウ マネージャーでの Windows 7 の Flip Mode Present のサポートと、関連する現在の統計情報について説明します。 ターゲット アプリケーションには、ビデオまたはフレーム レートベースのプレゼンテーション アプリケーションが含まれます。 Direct3D 9Ex Flip Mode Present を使用するアプリケーションでは、DWM を有効にするとシステム リソースの負荷が軽減されます。 Flip Mode Present に関連する現在の統計の機能強化により、Direct3D 9Ex アプリケーションでは、リアルタイムのフィードバックと修正メカニズムを提供することで、プレゼンテーションの速度をより適切に制御できます。 サンプル リソースに対する詳細な説明とポインターが含まれています。

このトピックは、次のセクションで構成されています。

Direct3D 9Ex for Windows 7 の改善された機能

Direct3D 9Ex のフリップ モード プレゼンテーションは、レンダリングされたイメージを Windows 7 Desktop Window Manager (DWM) に効率的に取り込んで合成する Direct3D 9Ex でイメージを表示する改善されたモードです。 Windows Vista 以降、DWM はデスクトップ全体を構成します。 DWM が有効になっている場合、ウィンドウ モード アプリケーションは、DWM に対して表示される Blt モード (または Blt モデル) というメソッドを使用して、デスクトップにコンテンツを表示します。 Blt Model では、DWM はデスクトップコンポジション用の Direct3D 9Ex レンダリングサーフェスのコピーを保持します。 アプリケーションが更新されると、新しいコンテンツが blt を介して DWM サーフェスにコピーされます。 Direct3D および GDI コンテンツを含むアプリケーションの場合、GDI データも DWM サーフェイスにコピーされます。

Windows 7 で使用できる Flip Mode Present to DWM (または Flip Model) は、基本的にウィンドウ モード アプリケーションと DWM の間でアプリケーション サーフェスのハンドルを渡すことを可能にする新しいプレゼンテーション 方法です。 Flip Model では、リソースの節約に加えて、高度な現在の統計がサポートされています。

現在の統計情報は、アプリケーションがビデオストリームとオーディオストリームを同期し、ビデオ再生の不具合から回復するために使用できるフレームタイミング情報です。 現在の統計情報のフレーム タイミング情報を使用すると、アプリケーションはビデオ フレームの表示速度を調整して、よりスムーズにプレゼンテーションできます。 WINDOWS Vista では、DWM がデスクトップコンポジションのフレーム サーフェスの対応するコピーを保持している場合、アプリケーションは DWM によって提供される現在の統計情報を使用できます。 現在の統計情報を取得するこの方法は、既存のアプリケーションの Windows 7 で引き続き使用できます。

Windows 7 では、Flip Model を採用する Direct3D 9Ex ベースのアプリケーションでは、D3D9Ex API を使用して現在の統計情報を取得する必要があります。 DWM が有効になっている場合、ウィンドウ モードと全画面表示排他モード Direct3D 9Ex アプリケーションは、Flip Model を使用する場合と同じ現在の統計情報を期待できます。 Direct3D 9Ex Flip Model present statistics を使用すると、フレームが画面に表示された後ではなく、アプリケーションがリアルタイムで現在の統計情報を照会できます。ウィンドウモードFlip-Model有効なアプリケーションでは、全画面表示アプリケーションと同じ現在の統計情報を使用できます。D3D9Ex API に追加されたフラグを使用すると、Flip Model アプリケーションはプレゼンテーション時に遅延フレームを効果的に破棄できます。

Direct3D 9Ex Flip Model は、Windows 7 を対象とする新しいビデオまたはフレーム レートベースのプレゼンテーション アプリケーションで使用する必要があります。 DWM と Direct3D 9Ex ランタイムの間の同期のため、Flip Model を使用するアプリケーションでは、スムーズな表示を確保するために 2 から 4 個のバックバッファーを指定する必要があります。 現在の統計情報を使用するアプリケーションでは、Flip Model 対応の現在の統計情報の機能強化を使用するとメリットがあります。

Direct3D 9EX フリップ モード プレゼンテーション

Direct3D 9Ex Flip Mode Present のパフォーマンスの向上は、DWM がオンのときや、アプリケーションがウィンドウ モードの場合に、全画面表示の排他モードではなく、システム上で重要です。 次の表と図は、メモリ帯域幅の使用量と、Flip Model と既定の使用 Blt Model を選択したウィンドウ アプリケーションのシステム読み取りと書き込みの簡単な比較を示しています。

DWM に存在する Blt モード DWM に存在する D3D9Ex フリップ モード
1. アプリケーションがフレームを更新する (書き込み)
1. アプリケーションがフレームを更新する (書き込み)
2. Direct3D ランタイムは、サーフェスの内容を DWM リダイレクト 画面にコピーします (読み取り、書き込み)
2. Direct3D ランタイムがアプリケーション画面を DWM に渡す
3. 共有サーフェスのコピーが完了すると、DWM によってアプリケーション 画面が画面にレンダリングされます (読み取り、書き込み)
3. DWM は、アプリケーション画面を画面にレンダリングします (読み取り、書き込み)

blt モデルとフリップ モデルの比較の図

Flip Mode Present は、DWM によるウィンドウ フレームコンポジションの Direct3D ランタイムによる読み取りと書き込みの数を減らすことで、システム メモリの使用量を削減します。 これにより、システムの電力消費量と全体的なメモリ使用量が削減されます。

アプリケーションは、アプリケーションがウィンドウ モードか全画面表示排他モードかに関係なく、DWM がオンの場合に Direct3D 9Ex フリップ モードの現在の統計情報の機能強化を利用できます。

プログラミング モデルと API

Windows 7 で Direct3D 9Ex API を使用する新しいビデオまたはフレーム レート測定アプリケーションでは、メモリと省電力、および Windows 7 で実行するときに Flip Mode Present によって提供される改善されたプレゼンテーションを利用できます。 (以前のバージョンの Windows で実行されている場合、Direct3D ランタイムは既定でアプリケーションを Blt モード Present に設定します)。

Flip Mode Present では、DWM がオンのときに、アプリケーションでリアルタイムの現在の統計フィードバックと修正メカニズムを利用できます。 ただし、Flip Mode Present を使用するアプリケーションでは、同時 GDI API レンダリングを使用する場合の制限事項に注意する必要があります。

新しく開発されたアプリケーションと同じ利点と注意事項を使用して、既存のアプリケーションを変更して Flip Mode Present を利用できます。

Direct3D 9Ex フリップ モデルをオプトインする方法

Windows 7 を対象とする Direct3D 9Ex アプリケーションでは、 D3DSWAPEFFECT_FLIPEX 列挙値を使用してスワップ チェーンを作成することで、Flip Model をオプトインできます。 Flip Model をオプトインするために、アプリケーションは D3DPRESENT_PARAMETERS 構造体を指定し、 IDirect3D9Ex::CreateDeviceEx API を呼び出すときにこの構造体へのポインターを渡します。 このセクションでは、Windows 7 を対象とするアプリケーションで IDirect3D9Ex::CreateDeviceEx を使用して Flip Model をオプトインする方法について説明します。 IDirect3D9Ex::CreateDeviceEx API の詳細については、MSDN の「IDirect3D9Ex::CreateDeviceEx」を参照してください。

便宜上、 D3DPRESENT_PARAMETERSIDirect3D9Ex::CreateDeviceEx の構文がここで繰り返されます。

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);
typedef struct D3DPRESENT_PARAMETERS {
    UINT BackBufferWidth, BackBufferHeight;
    D3DFORMAT BackBufferFormat;
    UINT BackBufferCount;
    D3DMULTISAMPLE_TYPE MultiSampleType;
    DWORD MultiSampleQuality;
    D3DSWAPEFFECT SwapEffect;
    HWND hDeviceWindow;
    BOOL Windowed;
    BOOL EnableAutoDepthStencil;
    D3DFORMAT AutoDepthStencilFormat;
    DWORD Flags;
    UINT FullScreen_RefreshRateInHz;
    UINT PresentationInterval;
} D3DPRESENT_PARAMETERS, *LPD3DPRESENT_PARAMETERS;

Windows 7 用 Direct3D 9Ex アプリケーションを変更して Flip Model をオプトインする場合は、D3DPRESENT_PARAMETERSの指定されたメンバーに関する 次の項目を考慮する必要があります。

BackBufferCount

(Windows 7 のみ)

SwapEffect が新しいD3DSWAPEFFECT_FLIPEXスワップ チェーン効果の種類に設定されている場合、DWM によって解放される前の Present バッファーを待機した結果、アプリケーションのパフォーマンス低下を防ぐために、バック バッファーの数を 2 以上にする必要があります。

アプリケーションでD3DSWAPEFFECT_FLIPEXに関連付けられている現在の統計情報も使用する場合は、バック バッファーの数を 2 から 4 に設定することをお勧めします。

Windows Vista または以前のオペレーティング システムバージョンでD3DSWAPEFFECT_FLIPEXを使用すると、 CreateDeviceEx から失敗が返されます。

SwapEffect

(Windows 7 のみ)

新しいD3DSWAPEFFECT_FLIPEXスワップ チェーン効果の種類は、アプリケーションが DWM に Flip Mode Present を採用するタイミングを指定します。 これにより、アプリケーションはメモリと電力をより効率的に使用できます。また、アプリケーションはウィンドウ モードで全画面表示の現在の統計情報を利用できます。 全画面表示アプリケーションの動作は影響を受けません。 Windowed が TRUE に設定され、 SwapEffect がD3DSWAPEFFECT_FLIPEXに設定されている場合、ランタイムは 1 つの余分なバック バッファーを作成し、プレゼンテーション時にフロント バッファーになるバッファーに属するハンドルを回転します。

Flags

(Windows 7 のみ)

SwapEffect が新しいD3DSWAPEFFECT_FLIPEXスワップ チェーン効果の種類に設定されている場合、D3DPRESENTFLAG_LOCKABLE_BACKBUFFER フラグ設定できません。

Direct3D 9Ex Flip Model アプリケーションの設計ガイドライン

次のセクションのガイドラインを使用して、Direct3D 9Ex Flip Model アプリケーションを設計します。

Blt モードの存在とは別の HWND に存在する反転モードを使用する

アプリケーションでは、他の API (Blt Mode Present Direct3D 9Ex、Direct3D の他のバージョン、GDI など) も対象としていない HWND で Direct3D 9Ex Flip Mode Present を使用する必要があります。 Flip Mode Present は、子ウィンドウに表示するために使用できます。つまり、次の図に示すように、アプリケーションは、同じ HWND 内の Blt Model と混合されていない場合に Flip Model を使用できます。

direct3d 親ウィンドウと gdi 子ウィンドウの図(それぞれ独自の hwnd を持つ)

gdi 親ウィンドウと direct3d 子ウィンドウの図。それぞれ独自の hwnd

Blt Model ではサーフェスの追加のコピーが保持されるため、GDI およびその他の Direct3D コンテンツは、Direct3D と GDI からの段階的な更新を通じて同じ HWND に追加できます。 Flip Model を使用すると、DWM に渡 D3DSWAPEFFECT_FLIPEXスワップ チェーン 内の Direct3D 9Ex コンテンツのみが表示されます。 次の図に示すように、他のすべての Blt モデル Direct3D または GDI コンテンツの更新は無視されます。

フリップ モデルが使用され、direct3d と gdi コンテンツが同じ hwnd 内にある場合に表示されない可能性がある gdi テキストの図

dwm が有効で、アプリケーションがウィンドウ モードになっている direct3d および gdi コンテンツの図

したがって、Direct3D 9Ex Flip Model だけが HWND 全体にレンダリングされるスワップ チェーン バッファー サーフェスに対して Flip Model を有効にする必要があります。

GDI の ScrollWindow または ScrollWindowEx でフリップ モデルを使用しない

一部の Direct3D 9Ex アプリケーションでは、GDI の ScrollWindow 関数または ScrollWindowEx 関数を使用して、ユーザースクロール イベントがトリガーされたときにウィンドウの内容を更新します。 ScrollWindow と ScrollWindowEx は、ウィンドウがスクロールされるときに画面上のウィンドウの内容の blt を実行します。 これらの関数には、GDI および Direct3D 9Ex コンテンツに対する Blt モデルの更新も必要です。 いずれかの関数を使用するアプリケーションでは、アプリケーションがウィンドウ モードで DWM が有効になっている場合、画面にスクロールする表示ウィンドウの内容が必ずしも表示されるとは限りません。 アプリケーションでは GDI の ScrollWindow API と ScrollWindowEx API を使用せず、スクロールに応じて画面に内容を再描画することをお勧めします。

HWND ごとに 1 つのD3DSWAPEFFECT_FLIPEX スワップ チェーンを使用する

Flip Model を使用するアプリケーションでは、同じ HWND を対象とする複数の Flip Model スワップ チェーンを使用しないでください。

Direct3D 9Ex Flip Model アプリケーションのフレーム同期

現在の統計情報は、メディア アプリケーションがビデオとオーディオ ストリームを同期し、ビデオ再生の不具合から回復するために使用するフレーム タイミング情報です。 現在の統計の可用性を有効にするには、Direct3D 9Ex アプリケーションで、アプリケーションが IDirect3D9Ex::CreateDeviceEx に渡す BehaviorFlags パラメーターに、デバイス動作フラグ D3DCREATE_ENABLE_PRESENTSTATSが含まれていることを確認する必要があります。

便宜上、 IDirect3D9Ex::CreateDeviceEx の構文をここで繰り返します。

HRESULT CreateDeviceEx(
  UINT Adapter,
  D3DDEVTYPE DeviceType,
  HWND hFocusWindow,
  DWORD BehaviorFlags,
  D3DPRESENT_PARAMETERS* pPresentationParameters,
  D3DDISPLAYMODEEX *pFullscreenDisplayMode,
  IDirect3DDevice9Ex **ppReturnedDeviceInterface
);

Direct3D 9Ex Flip Model では 、D3DPRESENT_INTERVAL_IMMEDIATE プレゼンテーション フラグの動作を強制する D3DPRESENT_FORCEIMMEDIATE プレゼンテーション フラグが追加されます。 Direct3D 9Ex アプリケーションは、次に示すように、アプリケーションが IDirect3DDevice9Ex::P resentEx に渡す dwFlags パラメーターで、これらのプレゼンテーション フラグを指定します。

HRESULT PresentEx(
  CONST RECT *pSourceRect,
  CONST RECT *pDestRect,
  HWND hDestWindowOverride,
  CONST RGNDATA *pDirtyRegion,
  DWORD dwFlags
);

Windows 7 用 Direct3D 9Ex アプリケーションを変更する場合は、指定された D3DPRESENT プレゼンテーション フラグに関する次の情報を考慮する必要があります。

D3DPRESENT_DONOTFLIP

このフラグは、全画面表示モードまたは

(Windows 7 のみ)

アプリケーションが CreateDeviceEx の呼び出しでD3DSWAPEFFECT_FLIPEXD3DPRESENT_PARAMETERSSwapEffect メンバーを設定する場合。

D3DPRESENT_FORCEIMMEDIATE

(Windows 7 のみ)

このフラグは、アプリケーションが CreateDeviceEx の呼び出しでD3DSWAPEFFECT_FLIPEXにD3DPRESENT_PARAMETERSSwapEffect メンバーを設定する場合にのみ指定できます。 アプリケーションでは、このフラグを使用して、DWM Present キューの後半で複数のフレームを含むサーフェスを直ちに更新し、基本的に中間フレームをスキップできます。

Windowed FlipEx 対応アプリケーションでは、このフラグを使用して、DWM Present キューの後のフレームでサーフェスを直ちに更新し、中間フレームをスキップできます。 これは、遅延として検出されたフレームを破棄し、コンポジション時に後続のフレームを提示するメディア アプリケーションに特に役立ちます。 このフラグが正しく指定されていない場合、IDirect3DDevice9Ex::P resentEx は無効なパラメーター エラーを返します。

現在の統計情報を取得するために、アプリケーションは IDirect3DSwapChain9Ex::GetPresentStatistics API を呼び出して D3DPRESENTSTATS 構造体を取得します。

D3DPRESENTSTATS 構造体には、IDirect3DDevice9Ex::P resentEx 呼び出しに関する統計情報が含まれています。 デバイスは、D3DCREATE_ENABLE_PRESENTSTATS フラグを使用して IDirect3D9Ex::CreateDeviceEx 呼び出しを使用して作成する必要があります。 それ以外の場合、 GetPresentStatistics によって返されるデータは未定義です。 Flip-Model 対応 Direct3D 9Ex スワップ チェーンは、ウィンドウモードと全画面表示モードの両方で現在の統計情報を提供します。

ウィンドウ モードの Blt-Model 対応 Direct3D 9Ex スワップ チェーンの場合、 すべての D3DPRESENTSTATS 構造体値は 0 になります。

FlipEx present statistics の場合、 GetPresentStatistics は 次の状況でD3DERR_PRESENT_STATISTICS_DISJOINTを返します。

  • シーケンスの先頭を示す GetPresentStatistics の最初の呼び出し
  • DWM のオンからオフへの切り替え
  • モードの変更: 全画面表示または全画面表示から全画面表示に切り替えるウィンドウ モード

便宜上、 ここでは GetPresentStatistics の構文を繰り返します。

HRESULT GetPresentStatistics(
  D3DPRESENTSTATS * pPresentationStatistics
);

IDirect3DSwapChain9Ex::GetLastPresentCount メソッドは、最後の PresentCount (つまり、スワップ チェーンに関連付けられているディスプレイ デバイスによって行われた最後に成功した Present 呼び出しの Present ID) を返します。 この Present ID は、D3DPRESENTSTATS 構造体の PresentCount メンバーの値です。 Blt-Model 対応 Direct3D 9Ex スワップ チェーンの場合、ウィンドウ モードでは、すべての D3DPRESENTSTATS 構造体値は 0 になります。

便宜上、 IDirect3DSwapChain9Ex::GetLastPresentCount の構文がここで繰り返されます。

HRESULT GetLastPresentCount(
  UINT * pLastPresentCount
);

Windows 7 用 Direct3D 9Ex アプリケーションを変更する場合は、 D3DPRESENTSTATS 構造体に関する次の情報を考慮する必要があります。

  • GetLastPresentCount が返す PresentCount 値は、dwFlags パラメーターで指定されたD3DPRESENT_DONOTWAITを持つ PresentEx 呼び出しでエラーが返された場合、更新されません。
  • PresentEx がD3DPRESENT_DONOTFLIPで呼び出されると、GetPresentStatistics 呼び出しは成功しますが、アプリケーションがウィンドウ モードの場合、更新された D3DPRESENTSTATS 構造体は返されません。
  • D3DPRESENTSTATSPresentRefreshCount と SyncRefreshCount の比較:
    • PresentRefreshCount は、アプリケーションがすべての vsync に存在する場合、 SyncRefreshCount と等しくなります。
    • SyncRefreshCount は、存在が送信されたときの vsync 間隔で取得されます。 SyncQPCTime は、vsync 間隔に関連付けられているおおよその時間です。
typedef struct _D3DPRESENTSTATS {
    UINT PresentCount;
    UINT PresentRefreshCount;
    UINT SyncRefreshCount;
    LARGE_INTEGER SyncQPCTime;
    LARGE_INTEGER SyncGPUTime;
} D3DPRESENTSTATS;

DWM がオフの場合のウィンドウ アプリケーションのフレーム同期

DWM がオフの場合、ウィンドウ化されたアプリケーションはフリップ チェーンを経由せずにモニター画面に直接表示されます。 Windows Vista では、DWM がオフのときにウィンドウアプリケーションのフレーム統計情報を取得することはできません。 アプリケーションを DWM 対応にする必要がない API を維持するために、DWM がオフの場合、Windows 7 はウィンドウ化されたアプリケーションのフレーム統計情報を返します。 DWM がオフのときに返されるフレーム統計は、推定のみです。

Direct3D 9Ex フリップ モデルと現在の統計サンプルのWalk-Through

Direct3D 9Ex サンプルの FlipEx プレゼンテーションをオプトインするには

  1. サンプル アプリケーションが Windows 7 以降のオペレーティング システム バージョンで実行されていることを確認します。
  2. CreateDeviceEx の呼び出しで、D3DPRESENT_PARAMETERSSwapEffect メンバーをD3DSWAPEFFECT_FLIPEXに設定します。
    OSVERSIONINFO version;
    ZeroMemory(&version, sizeof(version));
    version.dwOSVersionInfoSize = sizeof(version);
    GetVersionEx(&version);
    
    // Sample would run only on Win7 or higher
    // Flip Model present and its associated present statistics behavior are only available on Windows 7 or higher operating system
    bool bIsWin7 = (version.dwMajorVersion > 6) || 
        ((version.dwMajorVersion == 6) && (version.dwMinorVersion >= 1));

    if (!bIsWin7)
    {
        MessageBox(NULL, L"This sample requires Windows 7 or higher", NULL, MB_OK);
        return 0;
    }

Direct3D 9Ex サンプルの FlipEx 関連の Present Statistics をオプトインするには

  • CreateDeviceExBehaviorFlags パラメーターでD3DCREATE_ENABLE_PRESENTSTATSを設定します。
    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_FLIPEX;        // Opts into Flip Model present for D3D9Ex swapchain
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = 256;                
    d3dpp.BackBufferHeight = 256;
    d3dpp.BackBufferCount = QUEUE_SIZE;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

    g_iWidth = d3dpp.BackBufferWidth;
    g_iHeight = d3dpp.BackBufferHeight;

    // Create the D3DDevice with present statistics enabled - set D3DCREATE_ENABLE_PRESENTSTATS for behaviorFlags parameter
    if(FAILED(g_pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_ENABLE_PRESENTSTATS,
                                      &d3dpp, NULL, &g_pd3dDevice)))
    {
        return E_FAIL;
    }

回避するには、グリッチを検出して回復する

  1. Queue Present の呼び出し: 推奨されるバックバッファー数は 2 から 4 です。

  2. Direct3D 9Ex サンプルでは、暗黙的なバックバッファーが追加されます。実際の Present キューの長さはバックバッファー数 + 1 です。

  3. ヘルパー Present キュー構造を作成して、正常に送信されたすべての Present の Present ID (PresentCount) と、関連する計算済み/予期される PresentRefreshCount を格納します。

  4. 不具合の発生を検出するには:

    • GetPresentStatistics を呼び出します。
    • 現在の統計情報が取得されたフレームのフレームが表示される現在の ID (PresentCount) と vsync 数 (PresentRefreshCount) を取得します。
    • Present ID に関連付けられている予想される PresentRefreshCount (サンプル コードの TargetRefresh) を取得します。
    • 実際の PresentRefreshCount が予想よりも遅い場合は、不具合が発生しました。
  5. グリッチから回復するには:

    • スキップするフレームの数を計算します (サンプル コードで iImmediates 変数をg_)。
    • 間隔D3DPRESENT_FORCEIMMEDIATEでスキップされたフレームを表示します。

グリッチ検出と回復に関する考慮事項

  1. グリッチ回復では、N (g_iQueueDelay) が Present キューの長さと g_iImmediates等しい Present 呼び出しの N (g_iQueueDelay変数) を受け取ります。これは次のとおりです。

    • [現在の間隔] D3DPRESENT_FORCEIMMEDIATEと共にフレームをスキップする
    • 処理する必要があるキューに登録されたプレゼンテーション
  2. グリッチの長さに制限を設定します (サンプルのGLITCH_RECOVERY_LIMIT)。 サンプル アプリケーションが長すぎるグリッチ (60Hz モニターでは 1 秒または 60 vsyncs など) から復旧できない場合は、断続的なアニメーションを飛び越えて Present ヘルパー キューをリセットします。

VOID Render()
{
    g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    g_pd3dDevice->BeginScene();

    // Compute new animation parameters for time and frame based animations

    // Time-based is a difference between base and current SyncRefreshCount
    g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
    // Frame-based is incrementing frame value
    g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;

    RenderBlurredMesh(TRUE);    // Time-based
    RenderBlurredMesh(FALSE);   // Frame-based

    g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;

    DrawText();

    g_pd3dDevice->EndScene();

    // Performs glitch recovery if glitch was detected
    if (g_bGlitchRecovery && (g_iImmediates > 0))
    {
        // If we have present immediates queued as a result of glitch detected, issue forceimmediate Presents for glitch recovery 
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE);
        g_iImmediates--;
        g_iShowingGlitchRecovery = MESSAGE_SHOW;
    }
    // Otherwise, Present normally
    else
    {
        g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, 0);
    }

    // Add to helper Present queue: PresentID + expected present refresh count of last submitted Present
    UINT PresentCount;
    g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
    g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);
    
    // QueueDelay specifies # Present calls to be processed before another glitch recovery attempt
    if (g_iQueueDelay > 0)
    {
        g_iQueueDelay--;
    }

    if (g_bGlitchRecovery)
    {
        // Additional DONOTFLIP presents for frame conversions, which basically follows the same logic, but without rendering
        for (DWORD i = 0; i < g_iDoNotFlipNum; i++)
        {
            if (g_TargetRefreshCount != -1)
            {
                g_TargetRefreshCount++;
                g_iFrameNumber++;
                g_aTimeBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_LastSyncRefreshCount - g_SyncRefreshCount;
                g_aFrameBasedHistory[g_iBlurHistoryCounter] = g_iStartFrame + g_iFrameNumber;
                g_iBlurHistoryCounter = (g_iBlurHistoryCounter + 1) % BLUR_FRAMES;
            }
            
            if (g_iImmediates > 0)
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_FORCEIMMEDIATE | D3DPRESENT_DONOTFLIP);
                g_iImmediates--;
            }
            else
            {
                g_pd3dDevice->PresentEx(NULL, NULL, NULL, NULL, D3DPRESENT_DONOTFLIP);
            }
            UINT PresentCount;
            g_pd3dSwapChain->GetLastPresentCount(&PresentCount);
            g_Queue.QueueFrame(PresentCount, g_TargetRefreshCount);

            if (g_iQueueDelay > 0)
            {
                g_iQueueDelay--;
            }
        }
    }

    // Check Present Stats info for glitch detection 
    D3DPRESENTSTATS PresentStats;

    // Obtain present statistics information for successfully displayed presents
    HRESULT hr = g_pd3dSwapChain->GetPresentStats(&PresentStats);

    if (SUCCEEDED(hr))
    {
        // Time-based update
        g_LastSyncRefreshCount = PresentStats.SyncRefreshCount;
        if ((g_SyncRefreshCount == -1) && (PresentStats.PresentCount != 0))
        {
            // First time SyncRefreshCount is reported, use it as base
            g_SyncRefreshCount = PresentStats.SyncRefreshCount;
        }

        // Fetch frame from the queue...
        UINT TargetRefresh = g_Queue.DequeueFrame(PresentStats.PresentCount);

        // If PresentStats returned a really old frame that we no longer have in the queue, just don't do any glitch detection
        if (TargetRefresh == FRAME_NOT_FOUND)
            return;

        if (g_TargetRefreshCount == -1)
        {
            // This is first time issued frame is confirmed by present stats, so fill target refresh count for all frames in the queue
            g_TargetRefreshCount = g_Queue.FillRefreshCounts(PresentStats.PresentCount, g_SyncRefreshCount);
        } 
        else
        {
            g_TargetRefreshCount++;
            g_iFrameNumber++;

            // To determine whether we're glitching, see if our estimated refresh count is confirmed
            // if the frame is displayed later than the expected vsync count
            if (TargetRefresh < PresentStats.PresentRefreshCount)
            {
                // then, glitch is detected!

                // If glitch is too big, don't bother recovering from it, just jump animation
                if ((PresentStats.PresentRefreshCount - TargetRefresh) > GLITCH_RECOVERY_LIMIT)
                {
                    g_iStartFrame += PresentStats.SyncRefreshCount - g_SyncRefreshCount;
                    ResetAnimation();
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;    
                } 
                // Otherwise, compute number of immediate presents to recover from it -- if we?re not still trying to recover from another glitch
                else if (g_iQueueDelay == 0)
                {
                      // skip frames to catch up to expected refresh count
                    g_iImmediates = PresentStats.PresentRefreshCount - TargetRefresh;
                    // QueueDelay specifies # Present calls before another glitch recovery 
                    g_iQueueDelay = g_iImmediates + QUEUE_SIZE;
                    if (g_bGlitchRecovery)
                        g_iGlitchesInaRow++;
                }
            }
            else
            {
                // No glitch, reset glitch count
                g_iGlitchesInaRow = 0;
            }
        }
    }
    else if (hr == D3DERR_PRESENT_STATISTICS_DISJOINT)
    {
        // D3DERR_PRESENT_STATISTICS_DISJOINT means measurements should be started from the scratch (could be caused by mode change or DWM on/off transition)
        ResetAnimation();
    }

    // If we got too many glitches in a row, reduce framerate conversion factor (that is, render less frames)
    if (g_iGlitchesInaRow == FRAMECONVERSION_GLITCH_LIMIT)
    {
        if (g_iDoNotFlipNum < FRAMECONVERSION_LIMIT)
        {
            g_iDoNotFlipNum++;
        }
        g_iGlitchesInaRow = 0;
        g_iShowingDoNotFlipBump = MESSAGE_SHOW;
    }
}

サンプル シナリオ

  • 次の図は、バックバッファー数が 4 のアプリケーションを示しています。 したがって、実際の Present キューの長さは 5 です。

    レンダリングされたフレームと現在のキューの図

    フレーム A は、同期間隔のカウントが 1 の画面に表示されるように設定されていますが、同期間隔カウント 4 に表示されたことが検出されました。 そのため、不具合が発生しました。 後続の 3 フレームには、D3DPRESENT_INTERVAL_FORCEIMMEDIATEが表示されます。 グリッチは、回復する前に合計 8 回の現在の呼び出しを受け取る必要があります。次のフレームは、ターゲットの同期間隔の数に従って表示されます。

フレーム同期のプログラミングに関する推奨事項の概要

  • すべての LastPresentCount ID ( GetLastPresentCount 経由で取得) と、送信されたすべての発表の推定 PresentRefreshCount のバックアップ リストを作成します。

    Note

    アプリケーションがD3DPRESENT_DONOTFLIPを使用して PresentEx を呼び出すと、 GetPresentStatistics 呼び出しは成功しますが、アプリケーションがウィンドウ モードのときに、更新された D3DPRESENTSTATS 構造体は返されません。

  • GetPresentStatistics を呼び出して、表示されるフレームの各 Present ID に関連付けられている実際の PresentRefreshCount を取得して、アプリケーションが呼び出しからエラーの戻り値を処理することを確認します。

  • 実際の PresentRefreshCount が推定 PresentRefreshCount より後の場合は、不具合が検出されます。 遅延フレームを送信することによって補正します' D3DPRESENT_FORCEIMMEDIATEを持つ存在.

  • 1 つのフレームが Present キューに遅れて表示されると、後続のすべてのキューに登録されたフレームが遅延して表示されます。 D3DPRESENT_FORCEIMMEDIATEは、キューに登録されたすべてのフレームの後に表示される次のフレームのみを修正します。 したがって、Present キューまたはバックバッファー数は長すぎないようにしてください。そのため、追いつくフレームの不具合が少なくなります。 最適なバックバッファー数は 2 から 4 です。

  • 推定 PresentRefreshCount が実際の PresentRefreshCount より後の場合、DWM 調整が発生している可能性があります。 次の解決策が可能です。

    • 現在のキューの長さを減らす
    • 現在のキューの長さを減らす以外に、GPU メモリ要件を他の方法で減らす (つまり、品質の低下、効果の削除など)
    • DWM 調整全般を防ぐために DwmEnableMMCSS を指定する
  • 次のシナリオで、アプリケーション表示機能とフレーム統計のパフォーマンスを確認します。

    • DWM のオンとオフを切り替えた状態
    • 全画面表示の排他モードとウィンドウモード
    • 低い機能のハードウェア
  • D3DPRESENT_FORCEIMMEDIATE Present を使用して多数のグリッチ フレームからアプリケーションを復旧できない場合は、次の操作を実行する可能性があります。

    • より少ないワークロードでレンダリングすることで、CPU と GPU の使用量を削減します。
    • ビデオデコードの場合は、品質を下げることでデコード速度が向上し、CPU と GPU の使用率が低下します。

Direct3D 9Ex の機能強化に関する結論

Windows 7 では、プレゼンテーション中にビデオまたはゲージのフレーム レートを表示するアプリケーションは、Flip Model にオプトインできます。 Flip Model Direct3D 9Ex に関連する現在の統計の改善は、フレーム レートごとのプレゼンテーションを同期するアプリケーションに役立ち、グリッチ検出と回復のためのリアルタイム フィードバックを使用できます。 Direct3D 9Ex Flip Model を採用する開発者は、GDI コンテンツとフレーム レートの同期とは別の HWND を対象とする必要があります。 このトピックの詳細と MSDN ドキュメントを参照してください。 その他のドキュメントについては、 MSDN の DirectX デベロッパー センターを参照してください。

行動への呼び掛け

プレゼンテーションのフレーム レートを同期したり、ディスプレイの不具合から回復したりしようとするアプリケーションを作成する場合は、Windows 7 で Direct3D 9Ex Flip Model とその現在の統計情報を使用することをお勧めします。

MSDN の DirectX デベロッパー センター