Direct3D9 コンテンツは、Windows Presentation Foundation (WPF) アプリケーションに含めることができます。 このトピックでは、WPF と効率的に相互運用できるように Direct3D9 コンテンツを作成する方法について説明します。
注
WPF で Direct3D9 コンテンツを使用する場合は、パフォーマンスについても考慮する必要があります。 パフォーマンスを最適化する方法の詳細については、「 Direct3D9 と WPF の相互運用性のパフォーマンスに関する考慮事項」を参照してください。
ディスプレイ バッファー
D3DImage クラスは、バック バッファーとフロント バッファーと呼ばれる 2 つの表示バッファーを管理します。 バック バッファーは Direct3D9 サーフェスです。 Unlock メソッドを呼び出すと、バック バッファーへの変更がフロント バッファーに転送されます。
次の図は、バック バッファーとフロント バッファーの関係を示しています。
Direct3D9 デバイスの作成
Direct3D9 コンテンツをレンダリングするには、Direct3D9 デバイスを作成する必要があります。 デバイスの作成に使用できる Direct3D9 オブジェクトには、 IDirect3D9
と IDirect3D9Ex
の 2 つがあります。 これらのオブジェクトを使用して、それぞれ IDirect3DDevice9
デバイスと IDirect3DDevice9Ex
デバイスを作成します。
次のいずれかのメソッドを呼び出して、デバイスを作成します。
IDirect3D9 * Direct3DCreate9(UINT SDKVersion);
HRESULT Direct3DCreate9Ex(UINT SDKVersion, IDirect3D9Ex **ppD3D);
Windows Vista 以降のオペレーティング システムでは、Windows ディスプレイ ドライバー モデル (WDDM) を使用するように構成されたディスプレイで Direct3DCreate9Ex
メソッドを使用します。 他のプラットフォームで Direct3DCreate9
メソッドを使用します。
Direct3DCreate9Ex メソッドの可用性
d3d9.dll には、Windows Vista 以降のオペレーティング システムでのみ Direct3DCreate9Ex
メソッドがあります。 Windows XP で関数を直接リンクすると、アプリケーションの読み込みに失敗します。
Direct3DCreate9Ex
メソッドがサポートされているかどうかを確認するには、DLL を読み込み、proc アドレスを探します。 次のコードは、 Direct3DCreate9Ex
メソッドをテストする方法を示しています。 完全なコード例については、「 チュートリアル: WPF でのホスティング用の Direct3D9 コンテンツの作成」を参照してください。
HRESULT
CRendererManager::EnsureD3DObjects()
{
HRESULT hr = S_OK;
HMODULE hD3D = NULL;
if (!m_pD3D)
{
hD3D = LoadLibrary(TEXT("d3d9.dll"));
DIRECT3DCREATE9EXFUNCTION pfnCreate9Ex = (DIRECT3DCREATE9EXFUNCTION)GetProcAddress(hD3D, "Direct3DCreate9Ex");
if (pfnCreate9Ex)
{
IFC((*pfnCreate9Ex)(D3D_SDK_VERSION, &m_pD3DEx));
IFC(m_pD3DEx->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast<void **>(&m_pD3D)));
}
else
{
m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!m_pD3D)
{
IFC(E_FAIL);
}
}
m_cAdapters = m_pD3D->GetAdapterCount();
}
Cleanup:
if (hD3D)
{
FreeLibrary(hD3D);
}
return hr;
}
HWND の作成
デバイスを作成するには HWND が必要です。 一般に、Direct3D9 で使用するダミー HWND を作成します。 次のコード例は、ダミー HWND を作成する方法を示しています。
HRESULT
CRendererManager::EnsureHWND()
{
HRESULT hr = S_OK;
if (!m_hwnd)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = DefWindowProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = NULL;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
IFC(E_FAIL);
}
m_hwnd = CreateWindow(szAppName,
TEXT("D3DImageSample"),
WS_OVERLAPPEDWINDOW,
0, // Initial X
0, // Initial Y
0, // Width
0, // Height
NULL,
NULL,
NULL,
NULL);
}
Cleanup:
return hr;
}
現在のパラメーター
デバイスを作成するには D3DPRESENT_PARAMETERS
構造体も必要ですが、重要なパラメーターはごくわずかです。 これらのパラメーターは、メモリ占有領域を最小限に抑えるために選択されます。
BackBufferHeight
フィールドとBackBufferWidth
フィールドを 1 に設定します。 0 に設定すると、HWND のディメンションに設定されます。
Direct3D9 で使用されるメモリの破損を防ぎ、Direct3D9 が FPU 設定を変更しないようにするには、常に D3DCREATE_MULTITHREADED
フラグと D3DCREATE_FPU_PRESERVE
フラグを設定します。
次のコードは、 D3DPRESENT_PARAMETERS
構造体を初期化する方法を示しています。
HRESULT
CRenderer::Init(IDirect3D9 *pD3D, IDirect3D9Ex *pD3DEx, HWND hwnd, UINT uAdapter)
{
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferHeight = 1;
d3dpp.BackBufferWidth = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
D3DCAPS9 caps;
DWORD dwVertexProcessing;
IFC(pD3D->GetDeviceCaps(uAdapter, D3DDEVTYPE_HAL, &caps));
if ((caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) == D3DDEVCAPS_HWTRANSFORMANDLIGHT)
{
dwVertexProcessing = D3DCREATE_HARDWARE_VERTEXPROCESSING;
}
else
{
dwVertexProcessing = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
}
if (pD3DEx)
{
IDirect3DDevice9Ex *pd3dDevice = NULL;
IFC(pD3DEx->CreateDeviceEx(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
NULL,
&m_pd3dDeviceEx
));
IFC(m_pd3dDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast<void**>(&m_pd3dDevice)));
}
else
{
assert(pD3D);
IFC(pD3D->CreateDevice(
uAdapter,
D3DDEVTYPE_HAL,
hwnd,
dwVertexProcessing | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE,
&d3dpp,
&m_pd3dDevice
));
}
Cleanup:
return hr;
}
バック バッファー レンダー ターゲットの作成
Direct3D9 コンテンツを D3DImageに表示するには、Direct3D9 サーフェスを作成し、 SetBackBuffer メソッドを呼び出して割り当てます。
アダプターのサポートの確認
サーフェスを作成する前に、すべてのアダプターが必要なサーフェス プロパティをサポートしていることを確認します。 1 つのアダプターのみにレンダリングする場合でも、システム内の任意のアダプターに WPF ウィンドウが表示される場合があります。 複数アダプター構成を処理する Direct3D9 コードは常に記述する必要があります。WPF は使用可能なアダプター間でサーフェスを移動する可能性があるため、すべてのアダプターのサポートを確認する必要があります。
次のコード例は、Direct3D9 のサポートについてシステム上のすべてのアダプターを確認する方法を示しています。
HRESULT
CRendererManager::TestSurfaceSettings()
{
HRESULT hr = S_OK;
D3DFORMAT fmt = m_fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8;
//
// We test all adapters because because we potentially use all adapters.
// But even if this sample only rendered to the default adapter, you
// should check all adapters because WPF may move your surface to
// another adapter for you!
//
for (UINT i = 0; i < m_cAdapters; ++i)
{
// Can we get HW rendering?
IFC(m_pD3D->CheckDeviceType(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
fmt,
TRUE
));
// Is the format okay?
IFC(m_pD3D->CheckDeviceFormat(
i,
D3DDEVTYPE_HAL,
D3DFMT_X8R8G8B8,
D3DUSAGE_RENDERTARGET | D3DUSAGE_DYNAMIC, // We'll use dynamic when on XP
D3DRTYPE_SURFACE,
fmt
));
// D3DImage only allows multisampling on 9Ex devices. If we can't
// multisample, overwrite the desired number of samples with 0.
if (m_pD3DEx && m_uNumSamples > 1)
{
assert(m_uNumSamples <= 16);
if (FAILED(m_pD3D->CheckDeviceMultiSampleType(
i,
D3DDEVTYPE_HAL,
fmt,
TRUE,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
NULL
)))
{
m_uNumSamples = 0;
}
}
else
{
m_uNumSamples = 0;
}
}
Cleanup:
return hr;
}
サーフェスの作成
サーフェスを作成する前に、デバイスの機能がターゲット オペレーティング システムで優れたパフォーマンスをサポートしていることを確認します。 詳細については、「パフォーマンスに関する Direct3D9 と WPF の相互運用性に関する考慮事項」を参照してください。
デバイスの機能を確認したら、サーフェスを作成できます。 次のコード例は、レンダー ターゲットを作成する方法を示しています。
HRESULT
CRenderer::CreateSurface(UINT uWidth, UINT uHeight, bool fUseAlpha, UINT m_uNumSamples)
{
HRESULT hr = S_OK;
SAFE_RELEASE(m_pd3dRTS);
IFC(m_pd3dDevice->CreateRenderTarget(
uWidth,
uHeight,
fUseAlpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
static_cast<D3DMULTISAMPLE_TYPE>(m_uNumSamples),
0,
m_pd3dDeviceEx ? FALSE : TRUE, // Lockable RT required for good XP perf
&m_pd3dRTS,
NULL
));
IFC(m_pd3dDevice->SetRenderTarget(0, m_pd3dRTS));
Cleanup:
return hr;
}
WDDM
WDDM を使用するように構成されている Windows Vista 以降のオペレーティング システムでは、レンダー ターゲット テクスチャを作成し、レベル 0 サーフェスを SetBackBuffer メソッドに渡すことができます。 ロック可能なレンダー ターゲット テクスチャを作成できず、パフォーマンスが低下するため、Windows XP ではこの方法はお勧めしません。
デバイスの状態の処理
D3DImage クラスは、バック バッファーとフロント バッファーと呼ばれる 2 つの表示バッファーを管理します。 バック バッファーは Direct3D サーフェスです。 バック バッファーへの変更は、 Unlock メソッドを呼び出すとフロント バッファーに転送され、ハードウェアに表示されます。 場合によっては、フロント バッファーが使用できなくなります。 この可用性の欠如は、画面ロック、全画面表示専用 Direct3D アプリケーション、ユーザー切り替え、またはその他のシステム アクティビティが原因で発生する可能性があります。 これが発生すると、WPF アプリケーションは IsFrontBufferAvailableChanged イベントを処理することによって通知されます。 アプリケーションがフロント バッファーに応答して使用できなくなる方法は、WPF がソフトウェア レンダリングにフォールバックできるかどうかによって異なります。 SetBackBuffer メソッドには、WPF がソフトウェア レンダリングにフォールバックするかどうかを指定するパラメーターを受け取るオーバーロードがあります。
SetBackBuffer(D3DResourceType, IntPtr)オーバーロードを呼び出すか、SetBackBuffer(D3DResourceType, IntPtr, Boolean)パラメーターをenableSoftwareFallback
に設定してfalse
オーバーロードを呼び出すと、フロント バッファーが使用できなくなったときに、レンダリング システムはバック バッファーへの参照を解放し、何も表示されません。 フロント バッファーが再び使用可能になると、レンダリング システムは WPF アプリケーションに通知するために IsFrontBufferAvailableChanged イベントを発生させます。
IsFrontBufferAvailableChanged イベントのイベント ハンドラーを作成して、有効な Direct3D サーフェスでレンダリングを再開できます。 レンダリングを再開するには、 SetBackBufferを呼び出す必要があります。
SetBackBuffer(D3DResourceType, IntPtr, Boolean) パラメーターを enableSoftwareFallback
に設定して true
オーバーロードを呼び出すと、フロント バッファーが使用できなくなったときにレンダリング システムはバック バッファーへの参照を保持するため、フロント バッファーが再度使用可能になったときにSetBackBufferを呼び出す必要はありません。
ソフトウェア レンダリングが有効になっていると、ユーザーのデバイスが使用できなくなる場合がありますが、レンダリング システムは Direct3D サーフェスへの参照を保持します。 Direct3D9 デバイスが使用できないかどうかを確認するには、 TestCooperativeLevel
メソッドを呼び出します。 Direct3D9Ex デバイスを確認するには、 CheckDeviceState
メソッドを呼び出します。これは、 TestCooperativeLevel
メソッドは非推奨であり、常に成功を返すからです。 ユーザー デバイスが使用できなくなった場合は、 SetBackBuffer を呼び出して、WPF のバック バッファーへの参照を解放します。 デバイスをリセットする必要がある場合は、SetBackBuffer パラメーターを backBuffer
に設定してnull
を呼び出し、SetBackBuffer有効な Direct3D サーフェスに設定してbackBuffer
をもう一度呼び出します。
Reset
メソッドを呼び出して、複数アダプターのサポートを実装している場合にのみ、無効なデバイスから回復します。 それ以外の場合は、すべての Direct3D9 インターフェイスを解放し、完全に再作成します。 アダプターのレイアウトが変更された場合、変更前に作成された Direct3D9 オブジェクトは更新されません。
サイズ変更の管理
D3DImageがネイティブ サイズ以外の解像度で表示される場合、BitmapScalingModeがBilinearに置き換えられる点を除き、現在のFantに従ってスケーリングされます。
より高い再現性が必要な場合は、 D3DImage のコンテナーのサイズが変更されたときに新しいサーフェスを作成する必要があります。
サイズ変更を処理するには、3 つの方法があります。
レイアウト システムに参加し、サイズが変更されたときに新しいサーフェスを作成します。 ビデオ メモリを使い果たしたり断片化したりする可能性があるため、サーフェスを作成しすぎないようにしてください。
サイズ変更イベントが一定期間発生しないまで待って、新しいサーフェスを作成します。
コンテナーのディメンションを 1 秒に数回チェックする DispatcherTimer を作成します。
マルチモニターの最適化
レンダリング システムが D3DImage を別のモニターに移動すると、パフォーマンスが大幅に低下する可能性があります。
WDDM では、モニターが同じビデオ カード上にあり、 Direct3DCreate9Ex
を使用している限り、パフォーマンスは低下しません。 モニターが別々のビデオ カード上にある場合、パフォーマンスが低下します。 Windows XP では、パフォーマンスは常に低下します。
D3DImageが別のモニターに移動すると、対応するアダプターに新しいサーフェスを作成して、優れたパフォーマンスを復元できます。
パフォーマンスの低下を回避するには、マルチモニター ケース専用のコードを記述します。 次の一覧は、マルチモニター コードを記述する 1 つの方法を示しています。
D3DImage メソッドを使用して、画面空間内の
Visual.ProjectToScreen
のポイントを検索します。MonitorFromPoint
GDI メソッドを使用して、ポイントを表示しているモニターを見つけます。IDirect3D9::GetAdapterMonitor
メソッドを使用して、モニターがオンになっている Direct3D9 アダプターを見つけます。アダプターがバック バッファーを持つアダプターと同じでない場合は、新しいモニターに新しいバック バッファーを作成し、 D3DImage バック バッファーに割り当てます。
注
D3DImageがモニターをまたぐ場合、WDDM と同じアダプター上のIDirect3D9Ex
の場合を除き、パフォーマンスが低下します。 このような状況では、パフォーマンスを向上させる方法はありません。
次のコード例は、現在のモニターを検索する方法を示しています。
void
CRendererManager::SetAdapter(POINT screenSpacePoint)
{
CleanupInvalidDevices();
//
// After CleanupInvalidDevices, we may not have any D3D objects. Rather than
// recreate them here, ignore the adapter update and wait for render to recreate.
//
if (m_pD3D && m_rgRenderers)
{
HMONITOR hMon = MonitorFromPoint(screenSpacePoint, MONITOR_DEFAULTTONULL);
for (UINT i = 0; i < m_cAdapters; ++i)
{
if (hMon == m_pD3D->GetAdapterMonitor(i))
{
m_pCurrentRenderer = m_rgRenderers[i];
break;
}
}
}
}
D3DImage コンテナーのサイズまたは位置が変更されたときにモニターを更新するか、1 秒に数回更新するDispatcherTimer
を使用してモニターを更新します。
WPF ソフトウェア レンダリング
WPF は、次の状況でソフトウェアの UI スレッドで同期的にレンダリングされます。
このような状況のいずれかが発生すると、レンダリング システムは CopyBackBuffer メソッドを呼び出してハードウェア バッファーをソフトウェアにコピーします。 既定の実装では、 GetRenderTargetData
メソッドがサーフェスと共に呼び出されます。 この呼び出しはロック/ロック解除パターンの外部で発生するため、失敗する可能性があります。 この場合、 CopyBackBuffer
メソッドは null
を返し、画像は表示されません。
CopyBackBuffer メソッドをオーバーライドし、基本実装を呼び出し、null
を返す場合は、プレースホルダー BitmapSourceを返すことができます。
また、基本実装を呼び出す代わりに、独自のソフトウェア レンダリングを実装することもできます。
注
WPF がソフトウェアで完全にレンダリングされている場合、WPF にはフロント バッファーがないため、 D3DImage は表示されません。
こちらも参照ください
- D3DImage
- Direct3D9 と WPF の相互運用性に関する パフォーマンスに関する考慮事項
- チュートリアル: WPF でのホスティング用の Direct3D9 コンテンツの作成
- チュートリアル: WPF での Direct3D9 コンテンツのホスト
.NET Desktop feedback