Direct3D 9Ex の機能強化
このトピックでは、Windows 7 で追加されたフリップ モード プレゼンスのサポートと、Direct3D 9Ex およびデスクトップ ウィンドウ マネージャーにおける関連するプレゼンス統計について説明します。 対象アプリケーションには、ビデオまたはフレーム レート ベースのプレゼンテーション アプリケーションが含まれます。 Direct3D 9Ex Flip Mode Present を使用するアプリケーションは、DWM が有効な場合にシステム リソースの負荷を軽減します。 Flip Mode Present に関連する現在の統計の強化により、Direct3D 9Ex アプリケーションは、リアルタイムのフィードバックと修正メカニズムを提供することで、プレゼンテーションの速度をより適切に制御できるようになります。 詳細な説明とサンプル リソースへのポインタが含まれています。
このトピックは、次のセクションで構成されています。
- Windows 7 向け Direct3D 9Ex の改善点
- Direct3D 9EX フリップモードプレゼンテーション
- プログラミングモデルとAPI
- Direct3D 9Ex フリップ モデルと現在の統計サンプルのウォークスルー
- Direct3D 9Ex の改善に関する結論
- アクションの呼び出し
- 関連トピック
Windows 7 向け Direct3D 9Ex の改善点
Direct3D 9Ex のフリップ モード プレゼンテーションは、Direct3D 9Ex で画像を表示する改良モードで、レンダリングされた画像を Windows 7 デスクトップ ウィンドウ マネージャー (DWM) に効率的に渡して合成します。 Windows Vista 以降では、DWM がデスクトップ全体を構成します。 DWM が有効になっている場合、ウィンドウ モード アプリケーションは、Blt Mode Present to DWM (または Blt Model) と呼ばれる方法を使用して、デスクトップにコンテンツを表示します。 Blt モデルでは、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 フリップ モデルの現在の統計により、アプリケーションは、フレームが画面に表示された後ではなく、リアルタイムで現在の統計を照会できます。ウィンドウ モードのフリップ モデル対応アプリケーションでは、フル スクリーン アプリケーションと同じ現在の統計情報が利用できます。D3D9Ex API に追加されたフラグにより、フリップ モデル アプリケーションは、プレゼンテーション時に遅れたフレームを効果的に破棄できます。
Direct3D 9Ex Flip Model は、Windows 7 を対象とする新しいビデオまたはフレーム レート ベースのプレゼンテーション アプリケーションで使用する必要があります。 DWM と Direct3D 9Ex ランタイム間の同期のため、Flip Model を使用するアプリケーションでは、スムーズなプレゼンテーションを実現するために 2 ~ 4 個のバックバッファーを指定する必要があります。 現在の統計情報を使用するアプリケーションでは、Flip Model 対応の現在の統計拡張機能を使用することでメリットが得られます。
Direct3D 9EX フリップモードプレゼンテーション
Direct3D 9Ex Flip Mode Present のパフォーマンスの向上は、DWM がオンで、アプリケーションが全画面排他モードではなくウィンドウ モードの場合にシステム上で顕著になります。 次の表と図は、Flip モデルとデフォルトの使用法である Blt モデルを選択したウィンドウ アプリケーションのメモリ帯域幅の使用状況とシステムの読み取りと書き込みの簡略化された比較を示しています。
DWM に Blt モードが存在します | D3D9Ex フリップ モードを DWM に表示 |
---|---|
1. アプリケーションがフレームを更新する(書き込み) |
1. アプリケーションがフレームを更新する(書き込み) |
2. Direct3D ランタイムは、サーフェスの内容を DWM リダイレクト サーフェスにコピーします (読み取り、書き込み) |
2. Direct3DランタイムはアプリケーションサーフェスをDWMに渡す |
3. 共有サーフェスのコピーが完了すると、DWM はアプリケーション サーフェスを画面にレンダリングします (読み取り、書き込み) |
3. DWMはアプリケーションサーフェスを画面上にレンダリングします(読み取り、書き込み) |
Flip Mode Present は、DWM によるウィンドウ フレームの構成に対する Direct3D ランタイムによる読み取りと書き込みの回数を減らすことで、システム メモリの使用量を削減します。 これにより、システムの電力消費と全体的なメモリ使用量が削減されます。
アプリケーションは、ウィンドウ モードであるか全画面排他モードであるかに関係なく、DWM がオンの場合、Direct3D 9Ex フリップ モードの現在の統計拡張機能を利用できます。
プログラミングモデルとAPI
Windows 7 で Direct3D 9Ex API を使用する新しいビデオまたはフレーム レート測定アプリケーションは、Windows 7 で実行するときに、メモリと電力の節約、および Flip Mode Present によって提供される改善されたプレゼンテーションを活用できます。 (以前のバージョンの Windows で実行する場合、Direct3D ランタイムはアプリケーションをデフォルトで Blt モード プレゼンスに設定します。)
フリップ モード プレゼンスは、DWM がオンのときにアプリケーションがリアルタイムの現在の統計フィードバックおよび修正メカニズムを利用できることを意味します。 ただし、Flip Mode Present を使用するアプリケーションでは、同時 GDI API レンダリングを使用する場合の制限に注意する必要があります。
新しく開発されたアプリケーションと同じ利点と注意事項を備え、既存のアプリケーションを変更して Flip Mode Present を活用できます。
Direct3D 9Ex フリップ モデルを選択する方法
Windows 7 を対象とする Direct3D 9Ex アプリケーションは、 D3DSWAPEFFECT_FLIPEX 列挙値を使用してスワップ チェーンを作成することにより、フリップ モデルを選択できます。 フリップ モデルを選択するには、アプリケーションは D3DPRESENT_PARAMETERS 構造体を指定し、 IDirect3D9Ex::CreateDeviceEx API を呼び出すときにこの構造体へのポインターを渡します。 このセクションでは、Windows 7 を対象とするアプリケーションが IDirect3D9Ex::CreateDeviceEx を使用してフリップ モデルを選択する方法について説明します。 IDirect3D9Ex::CreateDeviceEx API の詳細については、MSDN の IDirect3D9Ex::CreateDeviceExを参照してください。
便宜上、 D3DPRESENT_PARAMETERS と IDirect3D9Ex::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 アプリケーションを変更してフリップ モデルを選択する場合は、 D3DPRESENT_PARAMETERS の指定されたメンバーについて次の項目を考慮する必要があります。
-
BackBufferCount
-
(Windows 7 のみ)
SwapEffect が新しい D3DSWAPEFFECT_FLIPEX スワップ チェーン効果タイプに設定されている場合、前の Present バッファーが DWM によって解放されるのを待機することによるアプリケーションのパフォーマンスの低下を防ぐために、バック バッファー カウントは 2 以上である必要があります。
アプリケーションが D3DSWAPEFFECT_FLIPEX に関連付けられた現在の統計も使用する場合は、バック バッファー数を 2 ~ 4 に設定することをお勧めします。
Windows Vista またはそれ以前のオペレーティング システム バージョンで D3DSWAPEFFECT_FLIPEX を使用すると、 CreateDeviceEx から失敗が返されます。
-
SwapEffect
-
(Windows 7 のみ)
新しい D3DSWAPEFFECT_FLIPEX スワップ チェーン効果タイプは、アプリケーションが DWM にフリップ モード プレゼンスを採用しているかどうかを指定します。 これにより、アプリケーションはメモリと電力をより効率的に使用できるようになり、ウィンドウ モードで全画面の現在の統計情報を活用できるようになります。 フルスクリーンアプリケーションの動作には影響しません。 Windowed が TRUE に設定され、 SwapEffect が D3DSWAPEFFECT_FLIPEX に設定されている場合、ランタイムは 1 つの追加のバック バッファーを作成し、プレゼンテーション時にフロント バッファーになるバッファーに属するハンドルを回転させます。
-
Flags
-
(Windows 7 のみ)
SwapEffect が新しい D3DSWAPEFFECT_FLIPEX スワップ チェーン エフェクト タイプに設定されている場合、 D3DPRESENTFLAG_LOCKABLE_BACKBUFFER フラグは設定できません。
Direct3D 9Ex フリップ モデル アプリケーションの設計ガイドライン
Direct3D 9Ex フリップ モデル アプリケーションを設計するには、次のセクションのガイドラインを使用します。
Blt モードとは別の HWND に存在するフリップ モードを使用する
アプリケーションは、Blt Mode Present Direct3D 9Ex、他のバージョンの Direct3D、または GDI などの他の API の対象ではない HWND で Direct3D 9Ex Flip Mode Present を使用する必要があります。 フリップ モード プレゼンスは、子ウィンドウに表示するために使用できます。つまり、次の図に示すように、アプリケーションは、同じ HWND 内で Blt モデルと混在していない場合にフリップ モデルを使用できます。
Blt モデルはサーフェスの追加コピーを維持するため、Direct3D と GDI からの段階的な更新を通じて、GDI とその他の Direct3D コンテンツを同じ HWND に追加できます。 フリップ モデルを使用すると、DWM に渡される D3DSWAPEFFECT_FLIPEX スワップ チェーン内の Direct3D 9Ex コンテンツのみが表示されます。 次の図に示すように、その他のすべての Blt モデル Direct3D または GDI コンテンツの更新は無視されます。
したがって、Direct3D 9Ex フリップ モデルのみが HWND 全体にレンダリングされるスワップ チェーン バッファー サーフェスでは、フリップ モデルを有効にする必要があります。
GDI の ScrollWindow または ScrollWindowEx で Flip Model を使用しないでください
一部の Direct3D 9Ex アプリケーションでは、ユーザーのスクロール イベントがトリガーされたときに、GDI の ScrollWindow または ScrollWindowEx 関数を使用してウィンドウの内容を更新します。 ScrollWindow と ScrollWindowEx は、ウィンドウがスクロールされるときに画面上のウィンドウ コンテンツの blt を実行します。 これらの関数では、GDI および Direct3D 9Ex コンテンツの Blt モデルの更新も必要です。 いずれかの機能を使用するアプリケーションでは、アプリケーションがウィンドウ モードで DWM が有効になっている場合、画面上でスクロールする可視ウィンドウの内容が必ずしも表示されるわけではありません。 アプリケーションでは GDI の ScrollWindow および ScrollWindowEx API を使用せず、代わりにスクロールに応じて画面上のコンテンツを再描画することをお勧めします。
HWND ごとに 1 つの D3DSWAPEFFECT_FLIPEX スワップ チェーンを使用する
Flip Model を使用するアプリケーションでは、同じ HWND をターゲットとする複数の Flip Model スワップ チェーンを使用しないでください。
Direct3D 9Ex フリップ モデル アプリケーションのフレーム同期
現在の統計は、メディア アプリケーションがビデオとオーディオ ストリームを同期し、ビデオ再生の不具合から回復するために使用するフレーム タイミング情報です。 現在の統計情報の利用を有効にするには、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 フリップ モデルでは、 D3DPRESENT_INTERVAL_IMMEDIATE プレゼンテーション フラグの動作を強制する D3DPRESENT_FORCEIMMEDIATE プレゼンテーション フラグが追加されます。 Direct3D 9Ex アプリケーションは、次に示すように、アプリケーションが IDirect3DDevice9Ex::PresentEx に渡す >dwFlags パラメータでこれらのプレゼンテーション フラグを指定します。
HRESULT PresentEx(
CONST RECT *pSourceRect,
CONST RECT *pDestRect,
HWND hDestWindowOverride,
CONST RGNDATA *pDirtyRegion,
DWORD dwFlags
);
Windows 7 用の Direct3D 9Ex アプリケーションを変更する場合は、指定された D3DPRESENT プレゼンテーション フラグに関する次の情報を考慮する必要があります。
-
このフラグはフルスクリーンモードまたは
(Windows 7 のみ)
アプリケーションが CreateDeviceEx の呼び出しで、 D3DPRESENT_PARAMETERS の SwapEffect メンバーを D3DSWAPEFFECT_FLIPEX に設定する場合。
-
(Windows 7 のみ)
このフラグは、アプリケーションが CreateDeviceEx の呼び出しで D3DPRESENT_PARAMETERS の SwapEffect メンバーを D3DSWAPEFFECT_FLIPEX に設定する場合にのみ指定できます。 アプリケーションはこのフラグを使用して、基本的に中間フレームをスキップして、DWM Present キュー内の後続のフレームでサーフェスを即座に更新できます。
ウィンドウ化された FlipEx 対応アプリケーションでは、このフラグを使用して、中間のフレームをスキップし、DWM Present キュー内の後のフレームでサーフェスをすぐに更新できます。 これは、遅れていると検出されたフレームを破棄し、後続のフレームを合成時に提示するメディア アプリケーションに特に役立ちます。 このフラグが不適切に指定されている場合、 IDirect3DDevice9Ex::PresentEx は無効なパラメータ エラーを返します。
現在の統計情報を取得するために、アプリケーションは IDirect3DSwapChain9Ex::GetPresentStatistics API を呼び出して D3DPRESENTSTATS 構造体を取得します。
D3DPRESENTSTATS 構造体には、 IDirect3DDevice9Ex::PresentEx 呼び出しに関する統計情報が含まれています。 デバイスは、 D3DCREATE_ENABLE_PRESENTSTATS フラグを指定した IDirect3D9Ex::CreateDeviceEx 呼び出しを使用して作成する必要があります。 それ以外の場合、 GetPresentStatistics によって返されるデータは未定義になります。 フリップモデル対応の Direct3D 9Ex スワップ チェーンは、ウィンドウ モードと全画面モードの両方で現在の統計情報を提供します。
ウィンドウ モードの Blt モデル対応 Direct3D 9Ex スワップ チェーンの場合、すべての D3DPRESENTSTATS 構造体の値はゼロになります。
FlipEx の現在の統計の場合、 GetPresentStatistics は次の状況で D3DERR_PRESENT_STATISTICS_DISJOINT を返します。
- GetPresentStatistics への最初の呼び出し。これはシーケンスの開始を示します。
- DWMのオンからオフへの遷移
- モードの変更: ウィンドウモードからフルスクリーンへ、またはフルスクリーンからフルスクリーンへの遷移
便宜上、 GetPresentStatistics の構文がここで繰り返されます。
HRESULT GetPresentStatistics(
D3DPRESENTSTATS * pPresentationStatistics
);
IDirect3DSwapChain9Ex::GetLastPresentCount メソッドは、最後の PresentCount、つまり、スワップ チェーンに関連付けられているディスプレイ デバイスによって行われた最後の成功した Present 呼び出しの Present ID を返します。 この Present ID は、 D3DPRESENTSTATS 構造体の PresentCount メンバーの値です。 Blt モデル対応の Direct3D 9Ex スワップ チェーンの場合、ウィンドウ モードでは、すべての D3DPRESENTSTATS 構造体の値はゼロになります。
便宜上、 IDirect3DSwapChain9Ex::GetLastPresentCount の構文がここで繰り返されます。
HRESULT GetLastPresentCount(
UINT * pLastPresentCount
);
Windows 7 用の Direct3D 9Ex アプリケーションを変更する場合は、 D3DPRESENTSTATS 構造に関する次の情報を考慮する必要があります。
- GetLastPresentCount が返す PresentCount 値は、 dwFlags パラメータで D3DPRESENT_DONOTWAIT が指定された PresentEx 呼び出しが失敗を返した場合、更新されません。
- PresentEx が D3DPRESENT_DONOTFLIP で呼び出されると、 GetPresentStatistics 呼び出しは成功しますが、アプリケーションがウィンドウ モードの場合、更新された D3DPRESENTSTATS 構造は返されません。
- PresentRefreshCount と SyncRefreshCount の比較 (D3DPRESENTSTATS):
- アプリケーションが vsync ごとに表示される場合、 PresentRefreshCount は 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 を維持するために、Windows 7 は、DWM がオフのときにウィンドウ アプリケーションのフレーム統計情報を返します。 DWM がオフのときに返されるフレーム統計は推定値のみです。
Direct3D 9Ex フリップ モデルと現在の統計サンプルのウォークスルー
Direct3D 9Ex サンプルの FlipEx プレゼンテーションを選択するには
- サンプル アプリケーションが Windows 7 以降のオペレーティング システム バージョンで実行されていることを確認します。
- CreateDeviceEx の呼び出しで、 D3DPRESENT_PARAMETERS の SwapEffect メンバーを 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関連の現在の統計情報もオプトインするには
- CreateDeviceExの BehaviorFlags パラメータに 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;
}
不具合を回避、検出、回復する
キュー Present 呼び出し: 推奨されるバックバッファ数は 2 ~ 4 です。
Direct3D 9Ex サンプルは暗黙的なバックバッファを追加します。実際の現在のキューの長さはバックバッファ数 + 1 です。
正常に送信されたすべてのプレゼントのプレゼント ID (PresentCount) と、関連付けられた計算済み/予想される PresentRefreshCount を格納するためのヘルパー プレゼント キュー構造を作成します。
グリッチの発生を検出するには:
- GetPresentStatisticsを呼び出します。
- 現在の統計が取得されるフレームの現在の ID (PresentCount) と、フレームが表示されている vsync カウント (PresentRefreshCount) を取得します。
- Present ID に関連付けられた予想される PresentRefreshCount (サンプル コードでは TargetRefresh) を取得します。
- 実際の PresentRefreshCount が予想よりも遅い場合は、不具合が発生しています。
不具合から回復するには:
- スキップするフレームの数を計算します (サンプル コードの g_ iImmediates 変数)。
- スキップされたフレームを間隔 D3DPRESENT_FORCEIMMEDIATE で表示します。
グリッチの検出と回復に関する考慮事項
グリッチ回復には、N (サンプル コードの g_iQueueDelay 変数) 回の Present 呼び出しが必要です。ここで、N (g_iQueueDelay) は、g_iImmediates と Present キューの長さの合計に等しくなります。つまり、次のようになります。
- フレームをスキップする(現在間隔D3DPRESENT_FORCEIMMEDIATE)
- 処理が必要なキュー内のプレゼント
グリッチの長さに制限を設定します (サンプルでは GLITCH_RECOVERY_LIMIT)。 サンプル アプリケーションが長すぎるグリッチ (たとえば、1 秒、または 60 Hz モニターで 60 回の vsync) から回復できない場合は、断続的なアニメーションをスキップして、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 回の Present 呼び出しが必要になります。次のフレームは、対象となる同期間隔カウントに従って表示されます。
フレーム同期に関するプログラミングレコメンデーションの概要
すべての LastPresentCount ID (GetLastPresentCount経由で取得) と、送信されたすべてのプレゼントの関連する推定 PresentRefreshCount のバックアップ リストを作成します。
Note
アプリケーションが D3DPRESENT_DONOTFLIP を使用して PresentEx を呼び出すと、 GetPresentStatistics 呼び出しは成功しますが、アプリケーションがウィンドウ モードの場合、更新された D3DPRESENTSTATS 構造は返されません。
アプリケーションが呼び出しからの失敗の戻りを処理することを確認するために、 GetPresentStatistics を呼び出して、表示されるフレームの各 Present ID に関連付けられた実際の PresentRefreshCount を取得します。
実際の PresentRefreshCount が推定された PresentRefreshCount よりも遅い場合、グリッチが検出されます。 遅れているフレームの Present を D3DPRESENT_FORCEIMMEDIATE で送信して補正します。
1 つのフレームが Present キューで遅れて表示されると、キュー内の後続のフレームもすべて遅れて表示されます。 D3DPRESENT_FORCEIMMEDIATE は、キューに入れられたすべてのフレームの後に表示される次のフレームのみを修正します。 したがって、Present キューまたはバックバッファ カウントは長すぎてはならず、フレームの不具合に対処する必要が少なくなります。 最適なバックバッファ数は 2 ~ 4 です。
推定された PresentRefreshCount が実際の PresentRefreshCount よりも遅い場合は、DWM スロットルが発生している可能性があります。 以下の解決策が考えられます。
- 現在のキューの長さを減らす
- プレゼンスキューの長さを減らす以外の手段(つまり、品質を下げる、効果を削除するなど)で GPU メモリ要件を減らす
- 一般的にDWMスロットリングを防ぐために DwmEnableMMCSS を指定する
次のシナリオでアプリケーションの表示機能とフレーム統計のパフォーマンスを確認します。
- DWMのオン/オフ
- フルスクリーン排他モードとウィンドウモード
- 低機能ハードウェア
D3DPRESENT_FORCEIMMEDIATE が存在する状態でアプリケーションが多数のグリッチ フレームから回復できない場合、次の操作を実行する可能性があります。
- 少ない負荷でレンダリングすることで、CPU と GPU の使用量を削減します。
- ビデオデコードの場合、品質を下げて CPU と GPU の使用率を下げることで、デコードを高速化します。
Direct3D 9Ex の改善に関する結論
Windows 7 では、プレゼンテーション中にビデオを表示したりフレーム レートを測定したりするアプリケーションで、Flip Model を選択できます。 Flip Model Direct3D 9Ex に関連する現在の統計の改善は、フレーム レートごとにプレゼンテーションを同期し、グリッチの検出と回復のためのリアルタイム フィードバックを提供するアプリケーションに役立ちます。 Direct3D 9Ex フリップ モデルを採用する開発者は、GDI コンテンツとは別の HWND をターゲットにすることと、フレーム レートの同期を考慮する必要があります。 詳細についてはこのトピックを参照してください。 追加のドキュメントについては、 MSDN の DirectX 開発者センターを参照してください。
アクションの呼び出し
プレゼンテーションのフレーム レートを同期したり、表示の不具合から回復したりするアプリケーションを作成する場合は、Direct3D 9Ex Flip Model と Windows 7 での現在の統計を使用することをお勧めします。