Windows グラフィックス API 間のサーフェス共有
このトピックでは、Direct3D 11、Direct2D、DirectWrite、Direct3D 10、Direct3D 9Ex などの Windows グラフィックス API 間のサーフェス共有を使用した相互運用性の技術的な概要について説明します。 これらの API に関する実用的な知識が既にある場合は、このホワイト ペーパーが、Windows 7 または Windows Vista オペレーティング システム用に設計されたアプリケーションで複数の API を使用して同じサーフェスにレンダリングする際に役立ちます。 このトピックでは、ベスト プラクティスのガイドラインと追加リソースへのポインタも提供します。
Note
DirectX 11.1 ランタイムでの Direct2D と DirectWrite の相互運用性のために、Direct2D デバイスとデバイス コンテキストを使用して Direct3D 11 デバイスに直接レンダリングできます。
このトピックは、次のセクションで構成されています。
はじめに
このドキュメントでは、Windows グラフィックス API の相互運用性とは、異なる API による同じレンダリング サーフェイスの共有を指します。 このような相互運用性により、アプリケーションは複数の Windows グラフィックス API を活用して魅力的なディスプレイを作成できるようになり、既存の API との互換性を維持することで新しいテクノロジへの移行が容易になります。
Windows 7 (および Windows 7 相互運用パック搭載 Windows Vista SP2、Vista 7IP) では、グラフィックス レンダリング API は、Direct3D 11、Direct2D、Direct3D 10.1、Direct3D 10.0、Direct3D 9Ex、Direct3D 9c 以前の Direct3D API、GDI および GDI+ です。 Windows Imaging Component (WIC) と DirectWrite は画像処理の関連テクノロジであり、Direct2D はテキスト レンダリングを実行します。 Direct3D 9c および Direct3D 9Ex に基づく DirectX ビデオ アクセラレーション API (DXVA) は、ビデオ処理に使用されます。
Windows グラフィックス API が Direct3D ベースへと進化するにつれて、Microsoft は API 間の相互運用性を確保するためにさらに努力を注いでいます。 新しく開発された Direct3D API と、Direct3D API に基づくより高レベルの API も、古い API との互換性を確保するために必要なサポートを提供します。 たとえば、Direct2D アプリケーションは、Direct3D 10.1 デバイスを共有することで Direct3D 10.1 を使用できます。 また、Direct3D 11、Direct2D、および Direct3D 10.1 API はすべて、これらの API 間の相互運用性を完全にサポートする同期された共有サーフェスを有効にする DirectX Graphics Infrastructure (DXGI) 1.1 を利用できます。 DXGI 1.1 ベースの API は、DXGI 1.1 サーフェイスから GDI デバイス コンテキストを取得することにより、GDI および関連付けによって GDI+ と相互運用します。
同期されていないサーフェス共有は、Direct3D 9Ex ランタイムでサポートされています。 DXVA ベースのビデオ アプリケーションは、Direct3D 9Ex および DXGI 相互運用性ヘルパーを使用して、コンピューティング シェーダー用の Direct3D 11 との Direct3D 9Ex ベースの DXVA 相互運用性を実現したり、2D コントロールやテキスト レンダリング用の Direct2D と相互運用したりできます。 WIC と DirectWrite は、GDI、Direct2D、および関連して他の Direct3D API とも相互運用できます。
Direct3D 10.0、Direct3D 9c、およびそれ以前の Direct3D ランタイムは共有サーフェスをサポートしていません。 システム メモリ コピーは、GDI または DXGI ベースの API との相互運用性のために引き続き使用されます。
このドキュメント内の相互運用性シナリオは、同じアプリケーション ウィンドウではなく、共有レンダリング サーフェイスにレンダリングする複数のグラフィックス API を参照していることに注意してください。 異なるサーフェスをターゲットとし、同じウィンドウに合成される個別の API の同期については、このホワイト ペーパーの範囲外です。
API 相互運用性の概要
Windows グラフィックス API のサーフェス共有の相互運用性は、API から API へのシナリオと対応する相互運用性機能の観点から説明できます。 Windows 7 以降、および 7IP を含む Windows Vista SP2 以降では、新しい API と関連ランタイムに Direct2D と関連テクノロジ (Direct3D 11 および DXGI 1.1) が含まれます。 Windows 7 では GDI パフォーマンスも向上しました。 Direct3D 10.1 は Windows Vista SP1 で導入されました。 次の図は、API 間の相互運用性のサポートを示しています。
この図では、矢印は、接続されている API から同じサーフェスにアクセスできる相互運用性シナリオを示しています。 青い矢印は、Windows Vista で導入された相互運用性メカニズムを示します。 緑色の矢印は、新しい API の相互運用性サポート、または古い API が新しい API と相互運用できるようにする改善を示します。 たとえば、緑の矢印は、デバイスの共有、同期された共有サーフェスのサポート、Direct3D 9Ex/DXGI 同期ヘルパー、互換性のあるサーフェスからの GDI デバイス コンテキストの取得を表します。
相互運用性のシナリオ
Windows 7 および Windows Vista 7IP の時点では、Windows グラフィックス API の主流製品は、同じ DXGI 1.1 サーフェイスにレンダリングする複数の API をサポートしています。
Direct3D 11、Direct3D 10.1、Direct2D — 相互の相互運用性
Direct3D 11、Direct3D 10.1、Direct2D API (および DirectWrite や WIC などの関連 API) は、Direct3D 10.1 デバイス共有または同期された共有サーフェスのいずれかを使用して相互運用できます。
Direct3D 10.1 デバイスと Direct2D の共有
Direct2D と Direct3D 10.1 間のデバイス共有により、アプリケーションは両方の API を使用して、同じ基盤となる Direct3D デバイス オブジェクトを使用して、同じ DXGI 1.1 サーフェイスにシームレスかつ効率的にレンダリングできます。 Direct2D は、Direct3D 10.1 および DXGI 1.1 ランタイム上に構築されているという事実を活用して、既存の Direct3D 10.1 デバイスを使用して Direct2D API を呼び出す機能を提供します。 次のコード スニペットは、Direct2D がデバイスに関連付けられた DXGI 1.1 サーフェスから Direct3D 10.1 デバイス レンダー ターゲットを取得する方法を示しています。 Direct3D 10.1 デバイス レンダー ターゲットは、BeginDraw API と EndDraw API の間で Direct2D 描画呼び出しを実行できます。
// Direct3D 10.1 Device and Swapchain creation
HRESULT hr = D3D10CreateDeviceandSwapChain1(
pAdapter,
DriverType,
Software,
D3D10_CREATE_DEVICE_BGRA_SUPPORT,
featureLevel,
D3D10_1_SDK_VERSION,
pSwapChainDesc,
&pSwapChain,
&pDevice
);
hr = pSwapChain->GetBuffer(
0,
__uuidof(IDXGISurface),
(void **)&pDXGIBackBuffer
));
// Direct3D 10.1 API rendering calls
...
hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&m_spD2DFactory
));
pD2DFactory->CreateDxgiSurfaceRenderTarget(
pDXGIBackBuffer,
&renderTargetProperties,
&pD2DBackBufferRenderTarget
));
...
pD2DBackBufferRenderTarget->BeginDraw();
//Direct2D API rendering calls
...
pD2DBackBufferRenderTarget->EndDraw();
pSwapChain->Present(0, 0);
注釈
- 関連付けられている Direct3D 10.1 デバイスは、BGRA 形式をサポートする必要があります。 そのデバイスは、パラメーター D3D10_CREATE_DEVICE_BGRA_SUPPORTで D3D10CreateDevice1 を呼び出すことによって作成されました。 BGRA 形式は、Direct3D 10 機能レベル 9.1 以降でサポートされています。
- アプリケーションでは、同じ Direct3D10.1 デバイスに関連付けられた複数の ID2D1RenderTargets を作成しないでください。
- 最適なパフォーマンスを得られるように、デバイスに関連付けられているテクスチャやサーフェスなど、常に少なくとも 1 つのリソースを保持します。
デバイス共有は、Direct3D 10.1 と Direct2D レンダリング API の両方で共有される 1 つのレンダリング デバイスのインプロセスシングルスレッド使用に適しています。 同期された共有サーフェスにより、Direct3D 10.1、Direct2D、および Direct3D 11 API で使用される複数のレンダリング デバイスをマルチスレッド、インプロセス、およびアウトオブプロセスで使用できるようになります。
Direct3D 10.1 と Direct2D の相互運用性を実現するもう 1 つの方法は、IDXGISurface から ID2D1Bitmap オブジェクトを作成する ID3D1RenderTarget::CreateSharedBitmap を使用することです。 Direct3D10.1 シーンをビットマップに書き込み、Direct2D でレンダリングできます。 詳細については、「ID2D1RenderTarget::CreateSharedBitmap メソッド」を参照してください。
Direct2D ソフトウェアのラスター化
Direct2D ソフトウェア レンダラーを使用する場合、たとえば、Direct2D レンダー ターゲットを作成するときに D2D1_RENDER_TARGET_USAGE で D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING を指定すると、Direct3D 10.1 とのデバイス共有はサポートされません。
Direct2D は WARP10 ソフトウェア ラスタライザーを使用して Direct3D 10 または Direct3D 11 とデバイスを共有できますが、パフォーマンスは大幅に低下します。
DXGI 1.1 同期共有サーフェス
Direct3D 11、Direct3D 10.1、および Direct2D API はすべて DXGI 1.1 を使用します。これは、2 つ以上の Direct3D デバイスによる同じビデオ メモリ サーフェス (DXGISurface1) の読み取りと書き込みを同期する機能を提供します。 同期された共有サーフェスを使用するレンダリング デバイスは、それぞれ同じプロセスまたはクロスプロセスで実行される Direct3D 10.1 デバイスまたは Direct3D 11 デバイスにすることができます。
アプリケーションは、Direct2D レンダー ターゲット オブジェクトから Direct3D 10.1 デバイスを取得することにより、同期された共有サーフェスを使用して、Direct3D 11 と Direct3D 10.1 などの任意の DXGI 1.1 ベースのデバイス間、または Direct3D 11 と Direct2D 間で相互運用できます。
Direct3D 10.1 以降の API で DXGI 1.1 を使用するには、DXGI 1.1 ファクトリ オブジェクトから列挙される DXGI 1.1 アダプター オブジェクトを使用して Direct3D デバイスが作成されていることを確認します。 CreateDXGIFactory1 を呼び出して IDXGIFactory1 オブジェクトを作成し、EnumAdapters1 を呼び出して IDXGIAdapter1 オブジェクトを列挙します。 IDXGIAdapter1 オブジェクトは、D3D10CreateDevice または D3D10CreateDeviceAndSwapChain 呼び出しの一部として渡す必要があります。 DXGI 1.1 API の詳細については、DXGI 「DXGI のプログラミング ガイド」を参照してください。
API
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
同期された共有リソースを作成するときは、D3D10_RESOURCE_MISC_FLAG に D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX を設定します。
typedef enum D3D10_RESOURCE_MISC_FLAG {
D3D10_RESOURCE_MISC_GENERATE_MIPS = 0x1L,
D3D10_RESOURCE_MISC_SHARED = 0x2L,
D3D10_RESOURCE_MISC_TEXTURECUBE = 0x4L,
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX = 0x10L,
D3D10_RESOURCE_MISC_GDI_COMPATIBLE = 0x20L,
} D3D10_RESOURCE_MISC_FLAG;
D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX
作成されたリソースを IDXGIKeyedMutex::AcquireSync および ReleaseSync API を使用して同期できるようにします。 D3D10_RESOURCE_MISC_FLAG パラメーターを受け取る次のリソース作成 Direct3D 10.1 API は、新しいフラグをサポートするように拡張されました。
- ID3D10Device1::CreateTexture1D
- ID3D10Device1::CreateTexture2D
- ID3D10Device1::CreateTexture3D
- ID3D10Device1::CreateBuffer
リストされている関数のいずれかが D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX フラグを設定して呼び出された場合、返されるインターフェイスで IDXGIKeyedMutex インターフェイスを照会できます。このインターフェイスは、サーフェスへのアクセスを同期するための AcquireSync および ReleaseSync API を実装します。 サーフェスを作成するデバイスと、サーフェスを開くその他のデバイス (OpenSharedResource を使用) は、サーフェスへのレンダリング コマンドの前に IDXGIKeyedMutex::AcquireSync を呼び出し、レンダリングが完了したら IDXGIKeyedMutex::ReleaseSync を呼び出す必要があります。
WARP デバイスと REF デバイスは、共有リソースをサポートしていません。 WARP または REF デバイスのいずれかでこのフラグを使用してリソースを作成しようとすると、作成メソッドは E_OUTOFMEMORY エラー コードを返します。
IDXGIKEYEDMUTEX インターフェイス
DXGI 1.1 の新しいインターフェイスである IDXGIKeyedMutex は、複数のデバイスで使用される共有リソースへの排他的アクセスを可能にするキー付きミューテックスを表します。 このインターフェイスとその 2 つのメソッド (AcquireSync と ReleaseSync) のリファレンス ドキュメントについては、「IDXGIKeyedMutex」を参照してください。
サンプル: 2 つの Direct3D 10.1 デバイス間での同期されたサーフェスの共有
次の例は、2 つの Direct3D 10.1 デバイス間でサーフェスを共有する方法を示しています。 同期された共有サーフェイスは、Direct3D10.1 デバイスによって作成されます。
// Create Sync Shared Surface using Direct3D10.1 Device 1.
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
// must match swapchain format in order to CopySubresourceRegion.
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
// creates 2D texture as a Synchronized Shared Surface.
desc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
ID3D10Texture2D* g_pShared = NULL;
g_pd3dDevice1->CreateTexture2D( &desc, NULL, &g_pShared );
// QI IDXGIResource interface to synchronized shared surface.
IDXGIResource* pDXGIResource = NULL;
g_pShared->QueryInterface(__uuidof(IDXGIResource), (LPVOID*) &pDXGIResource);
// obtain handle to IDXGIResource object.
pDXGIResource->GetSharedHandle(&g_hsharedHandle);
pDXGIResource->Release();
if ( !g_hsharedHandle )
return E_FAIL;
// QI IDXGIKeyedMutex interface of synchronized shared surface's resource handle.
hr = g_pShared->QueryInterface( __uuidof(IDXGIKeyedMutex),
(LPVOID*)&g_pDXGIKeyedMutex_dev1 );
If ( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev1 == NULL ) )
return E_FAIL;
同じ Direct3D10.1 デバイスは、AcquireSync を呼び出すことによってレンダリング用の同期された共有サーフェスを取得し、その後 ReleaseSync を呼び出すことによって他のデバイスのレンダリング用にサーフェスを解放できます。 同期された共有サーフェスを他の Direct3D デバイスと共有していない場合、作成者は同じキー値を使用して取得および解放することで、同期された共有サーフェスを取得および解放 (レンダリングを開始および終了) できます。
// Obtain handle to Sync Shared Surface created by Direct3D10.1 Device 1.
hr = g_pd3dDevice2->OpenSharedResource( g_hsharedHandle,__uuidof(ID3D10Texture2D),
(LPVOID*) &g_pdev2Shared);
if (FAILED (hr))
return hr;
hr = g_pdev2Shared->QueryInterface( __uuidof(IDXGIKeyedMutex),
(LPVOID*) &g_pDXGIKeyedMutex_dev2);
if( FAILED( hr ) || ( g_pDXGIKeyedMutex_dev2 == NULL ) )
return E_FAIL;
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2.
UINT acqKey = 1;
UINT relKey = 0;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev2->AcquireSync(acqKey, timeOut);
if ( result == WAIT_OBJECT_0 )
// Rendering calls using Device 2.
else
// Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev2->ReleaseSync(relKey));
if (result == WAIT_OBJECT_0)
return S_OK;
2 番目の Direct3D10.1 デバイスは、AcquireSync を呼び出すことによってレンダリング用の同期された共有サーフェスを取得し、その後 ReleaseSync を呼び出すことによって最初のデバイスのレンダリング用にサーフェスを解放できます。 デバイス 2 は、デバイス 1 による ReleaseSync 呼び出しで指定されたキー値と同じキー値を使用して、同期された共有サーフェスを取得できることに注意してください。
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1.
UINT acqKey = 0;
UINT relKey = 1;
DWORD timeOut = 5;
DWORD result = g_pDXGIKeyedMutex_dev1->AcquireSync(acqKey, timeOut);
if (result == WAIT_OBJECT_0)
// Rendering calls using Device 1.
else
// Handle unable to acquire shared surface error.
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(relKey));
if ( result == WAIT_OBJECT_0 )
return S_OK;
同じサーフェスを共有する追加のデバイスは、次の呼び出しに示すように、追加のキーを使用してサーフェスの取得と解放を順番に行うことができます。
// Within Device 1's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 1
result = g_pDXGIKeyedMutex_dev1->AcquireSync(0, timeOut);
// Rendering calls using Device 1
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(1);
...
////////////////////////////////////////////////////////////////////////////
// Within Device 2's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 2
result = g_pDXGIKeyedMutex_dev2->AcquireSync(1, timeOut);
// Rendering calls using Device 2
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(2);
////////////////////////////////////////////////////////////////////////////
// Within Device 3's process/thread:
// Rendering onto Sync Shared Surface from D3D10.1 Device 1 using D3D10.1 Device 3
result = g_pDXGIKeyedMutex_dev1->AcquireSync(2, timeOut);
// Rendering calls using Device 3
...
result = g_pDXGIKeyedMutex_dev1->ReleaseSync(0);
...
実際のアプリケーションでは、あるデバイスがサーフェスを共有する別のデバイスを待機することを防ぐために、常に中間サーフェスにレンダリングされ、その後、共有サーフェスにコピーされる可能性があることに注意してください。
Direct2D および Direct3D 11 で同期共有サーフェスを使用する
同様に、Direct3D 11 API と Direct3D 10.1 API 間の共有では、いずれかの API デバイスから同期された共有サーフェスを作成し、プロセス内またはプロセス外で他の API デバイスと共有できます。
Direct2D を使用するアプリケーションは、Direct3D 10.1 デバイスを共有し、同期された共有サーフェスを使用して、同じプロセスに属しているか異なるプロセスに属しているかに関係なく、Direct3D 11 またはその他の Direct3D 10.1 デバイスと相互運用できます。 ただし、単一プロセス、単一スレッドのアプリケーションの場合、デバイス共有は、Direct2D と Direct3D 10 または Direct3D 11 間の相互運用性を実現する最も高性能で効率的な方法です。
ソフトウェア ラスタライザー
アプリケーションがグラフィックス ハードウェア アクセラレーションを使用する代わりに、リファレンス ラスタライザーや WARP などの Direct3D または Direct2D ソフトウェア ラスタライザーを使用する場合、同期された共有サーフェスはサポートされません。
Direct3D 9Ex と DXGI ベースの API の相互運用性
Direct3D 9Ex API には、他の API が共有サーフェイスから読み取ることができるように、サーフェス共有の概念が含まれていました。 Direct3D 9Ex 共有サーフェイスに読み取りと書き込みを共有するには、アプリケーション自体に手動同期を追加する必要があります。
Direct3D 9Ex 共有サーフェスと手動同期ヘルパー
Direct3D 9Ex と Direct3D 10 または 11 の相互運用性における最も基本的なタスクは、単一のサーフェスを最初のデバイス (デバイス A) から 2 番目のデバイス (デバイス B) に渡すことです。これにより、デバイス B がサーフェスのハンドルを取得すると、デバイス A のレンダリングが完了していることが保証されます。 したがって、デバイス B は安心してこのサーフェスを使用できます。 これは、従来のプロデューサーとコンシューマーの問題によく似ています。このディスカッションでは、そのように問題をモデル化します。 サーフェスを使用し、その後それを放棄する最初のデバイスはプロデューサー (デバイス A) であり、最初に待機しているデバイスはコンシューマー (デバイス B) です。 現実世界のアプリケーションはこれよりも洗練されており、複数のプロデューサーとコンシューマーのビルディング ブロックを連結して、必要な機能を作成します。
プロデューサーとコンシューマーのビルディング ブロックは、サーフェスのキューを使用してヘルパーに実装されます。 サーフェスはプロデューサーによってキューに追加され、コンシューマーによってキューから削除されます。 ヘルパーには、ISurfaceQueue、ISurfaceProducer、ISurfaceConsumer の 3 つの COM インターフェイスが導入されています。
ヘルパーの高レベルの概要
ISurfaceQueue オブジェクトは、共有サーフェスを使用するための構成要素です。 初期化された Direct3D デバイスと、固定数の共有サーフェスを作成するための説明を使用して作成されます。 キュー オブジェクトは、リソースの作成とコードのオープンを管理します。 サーフェスの数とタイプは固定されており、サーフェスが作成されると、アプリケーションはサーフェスを追加または削除することはできません。
ISurfaceQueue オブジェクトの各インスタンスは、生成デバイスから消費デバイスにサーフェスを送信するために使用できる一種の一方通行を提供します。 このような一方通行の道路を複数使用することで、特定のアプリケーションのデバイス間でのサーフェス共有シナリオが可能になります。
作成/オブジェクトの有効期間
キュー オブジェクトを作成するには、CreateSurfaceQueue を使用する方法と、ISurfaceQueue の Clone メソッドを使用する方法の 2 つがあります。 インターフェイスは COM オブジェクトであるため、標準の COM 有効期間管理が適用されます。
プロデューサー/コンシューマー モデル
Enqueue (): プロデューサーは、サーフェスの処理が完了し、別のデバイスで使用できるようになることを示すためにこの関数を呼び出します。 この関数から戻ると、プロデューサー デバイスはサーフェスに対する権限を失い、引き続き使用するのは安全ではありません。
Dequeue (): 使用しているデバイスは、この関数を呼び出して共有サーフェスを取得します。 API は、キューから取り出されたサーフェスが使用可能であることを保証します。
Metadata
API では、メタデータと共有サーフェスの関連付けをサポートしています。
Enqueue() には、使用するデバイスに渡される追加のメタデータを指定するオプションがあります。 メタデータは、作成時に既知の最大値より小さくする必要があります。
Dequeue() は、必要に応じてバッファーとバッファーのサイズへのポインターを渡すことができます。 キューは、対応する Enqueue 呼び出しからのメタデータをバッファーに入力します。
複製
各 ISurfaceQueue オブジェクトは、一方向の同期を解決します。 この API を使用するアプリケーションの大部分では、閉じたシステムが使用されることを前提としています。 2 つのデバイスがサーフェスを送受信する最も単純なクローズド システムには、2 つのキューが必要です。 ISurfaceQueue オブジェクトには Clone() メソッドがあり、同じ大きなパイプラインの一部である複数のキューを作成できます。
Clone は既存の ISurfaceQueue オブジェクトから新しい ISurfaceQueue オブジェクトを作成し、開いているすべてのリソースをそれらの間で共有します。 結果のオブジェクトは、ソース キューとまったく同じサーフェスを持ちます。 複製されたキューは、互いに異なるメタデータ サイズを持つことができます。
Surfaces
ISurfaceQueue は、サーフェスの作成と管理を担当します。 任意のサーフェスをキューに追加することはできません。 さらに、サーフェスにはアクティブな "所有者" が 1 つだけ必要です。特定のキュー上にあるか、特定のデバイスで使用されている必要があります。 複数のキューにサーフェスを配置したり、サーフェスがキューに入れられた後もデバイスがサーフェスの使用を継続したりすることは無効です。
API の詳細
IsurfaceQueue
キューは、共有リソースの作成と保守を担当します。 また、Clone を使用して複数のキューをチェーンする機能も提供します。 キューには、生成デバイスと消費デバイスを開くメソッドがあります。 いつでも開くことができるのは、それぞれ 1 つだけです。
キューは、次の API を公開します。
API | 説明 |
---|---|
CreateSurfaceQueue | ISurfaceQueue オブジェクト (「ルート」キュー) を作成します。 |
ISurfaceQueue::OpenConsumer | 消費デバイスをキューから解除するためのインターフェースを返します。 |
ISurfaceQueue::OpenProducer | キューに追加する生成デバイスのインターフェイスを返します。 |
ISurfaceQueue::Clone | ルート キュー オブジェクトとサーフェスを共有する ISurfaceQueue オブジェクトを作成します。 |
CreateSurfaceQueue
typedef struct SURFACE_QUEUE_DESC {
UINT Width;
UINT Height;
DXGI_FORMAT Format;
UINT NumSurfaces;
UINT MetaDataSize;
DWORD Flags;
} SURFACE_QUEUE_DESC;
メンバー
Width、Height 共有サーフェスの寸法。 すべての共有サーフェスの寸法は同じである必要があります。
Format共有サーフェスの形式。 すべての共有サーフェスの形式は同じである必要があります。 異なるデバイスのペアは異なる形式タイプを共有できるため、有効な形式は使用されるデバイスによって異なります。
NumSurfaces キューに含まれるサーフェスの数。 これは固定数値です。
MetaDataSize メタデータ バッファーの最大サイズ。
Flags キューの動作を制御するためのフラグ。 「解説」を参照してください。
HRESULT CreateSurfaceQueue(
[in] SURFACE_QUEUE_DESC *pDesc,
[in] IUnknown *pDevice,
[out] IDXGIXSurfaceQueue **ppQueue
);
パラメーター
pDesc [in] 作成する共有サーフェス キューの説明。
pDevice [in] 共有サーフェスの作成に使用するデバイス。 これは、Windows Vista の機能が原因で明示的なパラメーターです。 Direct3D 9 と Direct3D 10 の間で共有されるサーフェスの場合は、Direct3D 9 でサーフェスを作成する必要があります。
ppQueue [out] 戻り値には、ISurfaceQueue オブジェクトへのポインターが含まれます。
戻り値
pDevice がリソースを共有できない場合、この関数は DXGI_ERROR_INVALID_CALL を返します。 この関数は、リソースを作成します。 失敗した場合はエラーを返します。 成功した場合は、S_OK を返します。
注釈
キュー オブジェクトを作成すると、すべてのサーフェスも作成されます。 すべてのサーフェスは 2D レンダリング ターゲットであると想定され、D3D10_BIND_RENDER_TARGET および D3D10_BIND_SHADER_RESOURCE フラグ (または異なるランタイムの同等のフラグ) が設定されて作成されます。
開発者は、キューが複数のスレッドによってアクセスされるかどうかを示すフラグを指定できます。 フラグが設定されていない場合 (Flags == 0)、キューは複数のスレッドによって使用されます。 開発者はシングル スレッド アクセスを指定できます。これにより、同期コードがオフになり、このような場合にパフォーマンスが向上します。 複製された各キューには独自のフラグがあるため、システム内の異なるキューに異なる同期制御を持たせることができます。
プロデューサーを開く
HRESULT OpenProducer(
[in] IUnknown *pDevice,
[out] IDXGIXSurfaceProducer **ppProducer
);
パラメーター
pDevice [in]
サーフェスをサーフェス キューにエンキューするプロデューサー デバイス。
ppProducer [out] プロデューサー インターフェイスにオブジェクトを返します。
戻り値
デバイスがサーフェスを共有できない場合は、DXGI_ERROR_INVALID_CALL を返します。
コンシューマーを開く
HRESULT OpenConsumer(
[in] IUnknown *pDevice,
[out] IDXGIXSurfaceConsumer **ppConsumer
);
パラメーター
pDevice [in]
サーフェス キューからサーフェスをデキューするコンシューマー デバイス。
ppConsumer [out] オブジェクトをコンシューマー インターフェイスに返します。
戻り値
デバイスがサーフェスを共有できない場合は、DXGI_ERROR_INVALID_CALL を返します。
注釈
この関数は、入力デバイスのキュー内のすべてのサーフェスを開き、キャッシュします。 Dequeue への後続の呼び出しは、単にキャッシュに移動し、毎回サーフェスを再度開く必要はありません。
IDXGIXSurfaceQueue の複製
typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
UINT MetaDataSize;
DWORD Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;
Members MetaDataSize および Flags は、CreateSurfaceQueue の場合と同じ動作をします。
HRESULT Clone(
[in] SHARED_SURFACE_QUEUE_CLONE_DESC *pDesc,
[out] IDXGIXSurfaceQueue **ppQueue
);
パラメーター
pDesc [in] 作成する Clone オブジェクトの説明を提供する構造体。 このパラメーターは初期化する必要があります。
ppQueue [out] 初期化されたオブジェクトを返します。
注釈
ルートではない場合でも、既存のキュー オブジェクトから複製できます。
IDXGIXSurfaceConsumer
- IDirect3DDevice9 の場合、REFIID は __uuidof(IDirect3DTexture9) である必要があります。
- ID3D10Device の場合、REFIID は __uuidof(ID3D10Texture2D) である必要があります。
- ID3D11Device の場合、REFIID は __uuidof(ID3D11Texture2D) である必要があります。
HRESULT Dequeue(
[in] REFIID id,
[out] void **ppSurface,
[in,out] void *pBuffer,
[in,out] UINT *pBufferSize,
[in] DWORD dwTimeout
);
パラメーター
id [in]
消費デバイスの 2D サーフェスの REFIID。
ppSurface [out] サーフェスへのポインターを返します。
pBuffer [in, out] 省略可能なパラメーターであり、NULL でない場合、戻り値には、対応するエンキュー呼び出しで渡されたメタデータが含まれます。
pBufferSize [in, out] pBuffer のサイズ (バイト単位)。 pBuffer に返されたバイト数を返します。 エンキュー呼び出しでメタデータが提供されなかった場合、pBuffer は 0 に設定されます。
dwTimeout [in] タイムアウト値を指定します。 詳細については、「解説」を参照してください。
戻り値
タイムアウト値が指定されていて、関数がタイムアウト値より前に返されない場合、この関数は WAIT_TIMEOUT を返すことができます。 「解説」を参照してください。 使用できるサーフェスがない場合、関数は ppSurface が NULL に設定され、pBufferSize が 0 に設定され、戻り値が 0x80070120 (WIN32_TO_HRESULT(WAIT_TIMEOUT)) に設定された状態で返されます。
注釈
この API は、キューが空の場合にブロックできます。 dwTimeout パラメーターは、WaitForSingleObject などの Windows 同期 API と同じように動作します。 非ブロッキング動作の場合は、タイムアウト 0 を使用します。
ISurfaceProducer
このインターフェースは、アプリがサーフェスをキューに追加できるようにする 2 つのメソッドを提供します。 サーフェスがキューに追加されると、サーフェス ポインターは無効になり、安全に使用できなくなります。 アプリケーションがポインターに対して実行する必要がある唯一のアクションは、ポインターを解放することです。
Method | 説明 |
---|---|
ISurfaceProducer::Enqueue | サーフェスをキュー オブジェクトのキューに追加します。 この呼び出しが完了すると、プロデューサーはサーフェスの処理を終了し、サーフェスは別のデバイスの準備が整います。 |
ISurfaceProducer::Flush | アプリケーションに非ブロッキング動作が必要な場合に使用されます。 詳細については、「解説」を参照してください。 |
Enqueue
HRESULT Enqueue(
[in] IUnknown *pSurface,
[in] void *pBuffer,
[in] UINT BufferSize,
[in] DWORD Flags
);
パラメーター
pSurface [in]
キューに追加する必要がある生成デバイスのサーフェス。 このサーフェスは、同じキュー ネットワークから削除されたサーフェスである必要があります。 pBuffer [in] メタデータを渡すために使用される省略可能なパラメーター。 キュー解除呼び出しに渡されるデータを指す必要があります。
BufferSize [in] pBuffer のサイズ (バイト単位)。
Flags [in] この関数の動作を制御する省略可能なパラメーター。 唯一のフラグは SURFACE_QUEUE_FLAG_ DO_NOT_WAIT です。 フラッシュの「解説」を参照してください。 フラグが渡されない場合 (Flags == 0)、既定のブロック動作が使用されます。
戻り値
SURFACE_QUEUE_FLAG_DO_NOT_WAIT フラグが使用されている場合、この関数は DXGI_ERROR_WAS_STILL_DRAWING を返すことができます。
注釈
- この関数は、サーフェスをキューに追加します。 アプリケーションが SURFACE_QUEUE_FLAG_DO_NOT_WAIT を指定しない場合、この関数はブロックされ、キューに入れられたサーフェス上のすべてのレンダリングが完了していることを確認するために GPU-CPU 同期が実行されます。 この関数が成功すると、サーフェスをキューから削除できるようになります。 非ブロッキング動作が必要な場合は、DO_NOT_WAIT フラグを使用します。 詳細については、Flush() を参照してください。
- COM 参照カウントルールに従って、Dequeue によって返されるサーフェスは AddRef() になるため、アプリケーションでこれを行う必要はありません。 Enqueue を呼び出した後、アプリケーションはサーフェスを使用しなくなったため、サーフェスを解放する必要があります。
フラッシュ
HRESULT Flush(
[in] DWORD Flags,
[out] UINT *nSurfaces
);
パラメーター
Flags [in]
唯一のフラグは SURFACE_QUEUE_FLAG_ DO_NOT_WAIT です。 「解説」を参照してください。 nSurfaces [out] まだ保留中でフラッシュされていないサーフェスの数を返します。
戻り値
SURFACE_QUEUE_FLAG_DO_NOT_WAIT フラグが使用されている場合、この関数は DXGI_ERROR_WAS_STILL_DRAWING を返すことができます。 この関数は、サーフェスが正常にフラッシュされた場合に S_OK を返します。 この関数は、サーフェスがフラッシュされなかった場合にのみ DXGI_ERROR_WAS_STILL_DRAWING を返します。 戻り値と nSurfaces は、実行された作業と、実行する作業が残っている場合に、それをアプリケーションに示します。
注釈
Flush は、前の Enqueue 呼び出しで DO_NOT_WAIT フラグが使用された場合にのみ意味があります。それ以外の場合は、操作なしになります。 エンキューの呼び出しで DO_NOT_WAIT フラグが使用された場合、Enqueue はすぐに戻り、GPU と CPU の同期は保証されません。 サーフェスは依然としてキューに入れられているとみなされ、生成デバイスはそれを使用し続けることはできませんが、キューからの削除には使用できません。 デキューのサーフェスをコミットするには、Flush を呼び出す必要があります。 Flush は、現在エンキューされているすべてのサーフェスをコミットしようとします。 Flush にフラグが渡されない場合、キュー全体がブロックされてクリアされ、その中のすべてのサーフェスがキューからの削除用に準備されます。 DO_NOT_WAIT フラグが使用されている場合、キューはサーフェスをチェックして、準備ができているかどうかを確認します。この手順は非ブロッキングです。 GPU と CPU の同期が完了したサーフェスは、コンシューマー デバイスの準備が整っています。 まだ保留中のサーフェスは影響を受けません。 この関数は、フラッシュする必要があるサーフェスの数を返します。
Note
Flush によってキューのセマンティクスが中断されることはありません。 この API は、GPU と CPU 同期がいつ発生するかに関係なく、最初にキューに登録されたサーフェスが、後でキューに登録されたサーフェスよりも先にコミットされることを保証します。
Direct3D 9Ex と DXGI 相互運用ヘルパー: 使用方法
ほとんどの使用例では、2 つのデバイスが複数のサーフェスを共有することになると予想されます。 これは最も単純なシナリオでもあるため、このホワイト ペーパーでは、API を使用してこの目標を達成する方法を詳しく説明し、非ブロッキングのバリエーションについて説明し、最後に 3 つのデバイスの初期化に関する簡単なセクションで終わります。
2 つのデバイス
このヘルパーを使用するサンプル アプリケーションでは、Direct3D 9Ex と Direct3D 11 を一緒に使用できます。 アプリケーションは両方のデバイスでコンテンツを処理し、Direct3D 9 を使用してコンテンツを表示できます。 処理とは、コンテンツのレンダリング、ビデオのデコード、コンピュート シェーダーの実行などを意味します。 アプリケーションは、すべてのフレームに対して、最初に Direct3D 11 で処理し、次に Direct3D 9 で処理し、最後に Direct3D 9 で処理します。 さらに、Direct3D 11 を使用した処理では、Direct3D 9 で使用する必要があるメタデータがいくつか生成されます。 このセクションでは、このシーケンスに対応する 3 つの部分 (初期化、メイン ループ、クリーンアップ) のヘルパーの使用方法について説明します。
初期化
初期化には、次の手順が含まれます。
- 両方のデバイスを初期化します。
- ルート キューの作成: m_11to9Queue。
- ルート キューから複製する: m_9to11Queue。
- 両方のキューで OpenProducer/OpenConsumer を呼び出します。
キュー名では、番号 9 と 11 を使用して、プロデューサーである API とコンシューマーを示します。m_producertoconsumerQueue。 したがって、m_11to9Queueは、Direct3D 11 デバイスが Direct3D 9 デバイスが消費するサーフェスを生成するキューを示します。 同様に、m_9to11Queueは、Direct3D 9 が Direct3D 11 で使用するサーフェスを生成するキューを示します。
ルート キューは最初はいっぱいで、複製されたすべてのキューは最初は空です。 これは、Enqueue と Dequeue の最初のサイクルとメタデータの可用性を除き、アプリケーションにとって問題になりません。 Dequeue がメタデータを要求したが、何も設定されていない場合 (最初は何も存在しないか、Enqueueが何も設定しなかったため)、Dequeue はメタデータが受信されなかったと認識します。
両方のデバイスを初期化します。
m_pD3D9Device = InitializeD3D9ExDevice(); m_pD3D11Device = InitializeD3D11Device();
ルート キューを作成します。
この手順では、サーフェスも作成します。 サイズと形式の制限は、共有リソースの作成と同じです。 メタデータ バッファーのサイズは作成時に固定され、この場合は UINT を渡すだけです。
キューは、固定数のサーフェスで作成する必要があります。 パフォーマンスはシナリオによって異なります。 複数のサーフェスがあると、デバイスがビジー状態になる可能性が高くなります。 たとえば、サーフェスが 1 つしかない場合、2 つのデバイス間で並列処理は行われません。 一方、サーフェスの数を増やすとメモリフットプリントが増加し、パフォーマンスが低下する可能性があります。 この例では、2 つのサーフェスを使用します。SURFACE_QUEUE_DESC Desc; Desc.Width = 640; Desc.Height = 480; Desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; Desc.NumSurfaces = 2; Desc.MetaDataSize = sizeof(UINT); Desc.Flags = 0; CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
ルート キューを複製します。
複製された各キューは同じサーフェスを使用する必要がありますが、異なるメタデータ バッファー サイズと異なるフラグを持つことができます。 この場合、Direct3D 9 から Direct3D 11 へのメタデータはありません。SURFACE_QUEUE_CLONE_DESC Desc; Desc.MetaDataSize = 0; Desc.Flags = 0; m_11to9Queue->Clone(&Desc, &m_9to11Queue);
プロデューサーデバイスとコンシューマーデバイスを開きます。
アプリケーションは、Enqueue と Dequeue を呼び出す前にこの手順を実行する必要があります。 プロデューサーとコンシューマーを開くと、Enqueue/Dequeue API を含むインターフェイスが返されます。// Open for m_p9to11Queue. m_p9to11Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer); m_p9to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer); // Open for m_p11to9Queue. m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer); m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);
メイン ループ
キューの使用は、従来のプロデューサー/コンシューマーの問題の後にモデル化されます。 これは、デバイスごとの観点から考えてください。 各デバイスは、消費キューからサーフェスを取得するためにキューから削除し、サーフェス上で処理し、生成キューに配置する手順を実行する必要があります。 Direct3D 11 デバイスの場合、Direct3D 9 の使用方法はほぼ同じです。
// Direct3D 9 Device.
IDirect3DTexture9* pTexture9 = NULL;
REFIID surfaceID9 = _uuidof(IDirect3DTexture9);
UINT metaData;
UINT metaDataSize;
while (!done)
{
// Dequeue surface.
m_pD3D9Consumer->Dequeue(surfaceID9, (void**)&pSurface9,
&metaData, &metaDataSize, INFINITE);
// Process the surface.
ProcessD3D9(pSurface9);
// Present the surface using the meta data.
PresentD3D9(pSurface9, metaData, metaDataSize);
// Enqueue surface.
m_pD3D9Producer->Enqueue(pSurface9, NULL, 0, 0);
}
クリーンアップ
この手順は非常に簡単です。 Direct3D API をクリーンアップするための通常の手順に加えて、アプリケーションは返される COM インターフェイスを解放する必要があります。
m_pD3D9Producer->Release();
m_pD3D9Consumer->Release();
m_pD3D11Producer->Release();
m_pD3D11Consumer->Release();
m_p9to11Queue->Release();
m_p11to9Queue->Release();
非ブロッキングの使用
前の例は、各デバイスに独自のスレッドがあるマルチスレッドの使用ケースに適しています。 この例では、ブロッキング バージョンの API を使用しています。タイムアウトの場合は INFINITE、キューに入れるフラグはありません。 非ブロッキングの方法でヘルパーを使用する場合は、いくつかの変更のみを行う必要があります。 このセクションでは、1 つのスレッド上の両方のデバイスでの非ブロッキングの使用を示します。
初期化
初期化は、フラグを除いて同じです。 アプリケーションはシングル スレッドであるため、作成にはそのフラグを使用します。 これにより、一部の同期コードがオフになり、パフォーマンスが向上する可能性があります。
SURFACE_QUEUE_DESC Desc;
Desc.Width = 640;
Desc.Height = 480;
Desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags = SURFACE_QUEUE_FLAG_SINGLE_THREADED;
CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags = SURFACE_QUEUE_FLAG_SINGLE_THREADED;
m_11to9Queue->Clone(&Desc, &m_9to11Queue);
プロデューサー デバイスとコンシューマー デバイスを開く方法は、ブロックの例と同じです。
キューの使用
さまざまなパフォーマンス特性を持つ非ブロッキング形式でキューを使用する方法は多数あります。 次の例は単純ですが、過度のスピンとポーリングによりパフォーマンスが低下します。 これらの問題にもかかわらず、この例ではヘルパーの使用方法を示しています。 このアプローチでは、ループとデキュー、プロセス、エンキュー、フラッシュを常に実行します。 リソースが使用できないためにいずれかの手順が失敗した場合、アプリケーションは次のループを再試行するだけです。
// Direct3D 11 Device.
ID3D11Texture2D* pSurface11 = NULL;
REFIID surfaceID11 = __uuidof(ID3D11Texture2D);
UINT metaData;
while (!done)
{
//
// D3D11 Portion.
//
// Dequeue surface.
hr = m_pD3D11Consumer->Dequeue(surfaceID11,
(void**)&pSurface11,
NULL, 0, 0);
// Only continue if we got a surface.
if (SUCCEEDED(hr))
{
// Process the surface and return some meta data.
ProcessD3D11(pSurface11, &metaData);
// Enqueue surface.
m_pD3D11Producer->Enqueue(pSurface11, &metaData,
sizeof(UINT),
SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
}
// Flush the queue to check if any surfaces completed.
m_pD3D11Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
//
// Do the same with the Direct3D 9 Device.
//
// Dequeue surface.
hr = m_pD3D9Consumer->Dequeue(surfaceID9,
(void**)&pSurface9,
&metaData,
&metaDataSize, 0);
// Only continue if we got a surface.
if (SUCCEEDED(hr)))
{
// Process the surface.
ProcessD3D9(pSurface9);
// Present the surface using the meta data.
PresentD3D9(pSurface9, metaData, metaDataSize);
// Enqueue surface.
m_pD3D9Producer->Enqueue(pSurface9, NULL, 0,
SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
}
// Flush the queue to check if any surfaces completed.
m_pD3D9Producer->Flush(NULL,SURFACE_QUEUE_FLAG_DO_NOT_WAIT);
}
より複雑なソリューションでは、エンキューとフラッシュからの戻り値をチェックして、フラッシュが必要かどうかを判断できます。
3 つのデバイス
前の例を拡張して複数のデバイスをカバーするのは簡単です。 次のコードは初期化を実行します。 プロデューサー/コンシューマー オブジェクトが作成された後、それらを使用するコードは同じです。 この例には 3 つのデバイスがあるため、3 つのキューがあります。 サーフェスは Direct3D 9 から Direct3D 10 へ、そして Direct3D 11 へと流れます。
SURFACE_QUEUE_DESC Desc;
Desc.Width = 640;
Desc.Height = 480;
Desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
Desc.NumSurfaces = 2;
Desc.MetaDataSize = sizeof(UINT);
Desc.Flags = 0;
SURFACE_QUEUE_CLONE_DESC Desc;
Desc.MetaDataSize = 0;
Desc.Flags = 0;
CreateSurfaceQueue(&Desc, m_pD3D9Device, &m_11to9Queue);
m_11to9Queue->Clone(&Desc, &m_9to10Queue);
m_11to9Queue->Clone(&Desc, &m_10to11Queue);
前述したように、どのキューを複製しても、複製は同じように機能します。 たとえば、2 番目の Clone 呼び出しは、m_9to10Queue オブジェクトでオフになっている可能性があります。
// Open for m_p9to10Queue.
m_p9to10Queue->OpenProducer(m_pD3D9Device, &m_pD3D9Producer);
m_p9to10Queue->OpenConsumer(m_pD3D10Device, &m_pD3D10Consumer);
// Open for m_p10to11Queue.
m_p10to11Queue->OpenProducer(m_pD3D10Device, &m_pD3D10Producer);
m_p10to11Queue->OpenConsumer(m_pD3D11Device, &m_pD3D11Consumer);
// Open for m_p11to9Queue.
m_p11to9Queue->OpenProducer(m_pD3D11Device, &m_pD3D11Producer);
m_p11to9Queue->OpenConsumer(m_pD3D9Device, &m_pD3D9Consumer);
まとめ
相互運用性を使用して複数の DirectX API の機能を使用するソリューションを作成できます。 Windows グラフィックス API の相互運用性で、共通のサーフェス管理ランタイム DXGI 1.1 が提供されるようになりました。 このランタイムにより、Direct3D 11、Direct3D 10.1、Direct2D などの新しく開発された API 内での同期されたサーフェス共有のサポートが可能になります。 新しい API と既存の API 間の相互運用性の向上により、アプリケーションの移行と下位互換性が向上します。 Direct3D 9Ex と DXGI 1.1 コンシューマー API は相互運用できます。これは、アーカイブされた MSDN コード ギャラリー Microsoft サンプル リポジトリにある古い Win32 サンプル アプリで使用可能な同期メカニズムで示されています。