Windows 그래픽 API 간의 Surface 공유
이 항목에서는 Direct3D 11, Direct2D, DirectWrite, Direct3D 10 및 Direct3D 9Ex를 비롯한 Windows 그래픽 API 간의 표면 공유를 사용하여 상호 운용성에 대한 기술적인 개요를 제공합니다. 이러한 API에 대한 실무 지식이 이미 있는 경우 이 문서에서는 여러 API를 사용하여 Windows 7 또는 Windows Vista 운영 체제용으로 설계된 애플리케이션에서 동일한 화면으로 렌더링하는 데 도움이 될 수 있습니다. 또한 이 항목에서는 추가 리소스에 대한 모범 사례 지침 및 포인터를 제공합니다.
참고 항목
DirectX 11.1 런타임의 Direct2D 및 DirectWrite 상호 운용성의 경우 Direct2D 디바이스 및 디바이스 컨텍스트를 사용하여 Direct3D 11 디바이스에 직접 렌더링할 수 있습니다.
이 항목에는 다음과 같은 섹션이 포함되어 있습니다.
소개
이 문서에서 Windows 그래픽 API 상호 운용성은 다른 API에 의해 동일한 렌더링 화면의 공유를 나타냅니다. 이러한 종류의 상호 운용성을 통해 애플리케이션은 여러 Windows 그래픽 API를 활용하여 매력적인 디스플레이를 만들고 기존 API와의 호환성을 유지하여 새로운 기술로 쉽게 마이그레이션할 수 있습니다.
Windows 7(Windows 7 Interop Pack, Vista 7IP를 사용하는 Windows Vista SP2)에서 렌더링되는 API는 Direct3D 11, Direct2D, Direct3D 10.1, Direct3D 10.0, Direct3D 9Ex, Direct3D 9c 및 이전 Direct3D API뿐만 아니라 GDI 및 GDI+입니다. WIC(Windows 이미징 구성 요소) 및 DirectWrite는 이미지 처리와 관련된 기술이며 Direct2D는 텍스트 렌더링을 수행합니다. Direct3D 9c 및 Direct3D 9Ex를 기반으로 하는 DXVA(DirectX Video Acceleration API)는 비디오 처리에 사용됩니다.
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는 모두 DXGI(DirectX Graphics Infrastructure) 1.1을 활용할 수 있으므로 이러한 API 간의 상호 운용성을 완벽하게 지원하는 동기화된 공유 표면을 사용할 수 있습니다. DXGI 1.1 기반 API는 DXGI 1.1 표면에서 GDI 디바이스 컨텍스트를 가져와 GDI 및 GDI+와 연결에 의해 상호 운용됩니다.
비동기화 화면 공유는 Direct3D 9Ex 런타임에서 지원됩니다. DXVA 기반 비디오 애플리케이션은 Direct3D 9Ex 기반 DXVA 상호 운용성을 위해 Direct3D 9Ex 및 DXGI 상호 운용성 도우미를 컴퓨팅 셰이더용 Direct3D 11과 함께 사용하거나 2D 컨트롤 또는 텍스트 렌더링을 위해 Direct2D와 상호 운용할 수 있습니다. WIC 및 DirectWrite는 GDI, Direct2D 및 연결에 의해 다른 Direct3D API와도 상호 운용됩니다.
Direct3D 10.0, Direct3D 9c 및 이전 Direct3D 런타임은 공유 표면을 지원하지 않습니다. 시스템 메모리 복사본은 GDI 또는 DXGI 기반 API와의 상호 운용성을 위해 계속 사용됩니다.
이 문서의 상호 운용성 시나리오는 동일한 애플리케이션 창이 아닌 공유 렌더링 화면에 렌더링되는 여러 그래픽 API를 참조합니다. 동일한 창에 복합된 다른 표면을 대상으로 하는 별도의 API에 대한 동기화는 이 문서에서 다루지 않습니다.
API 상호 운용성 개요
Windows 그래픽 API의 Surface 공유 상호 운용성은 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는 Direct2D가 Direct3D 10.1 및 DXGI 1.1 런타임을 기반으로 빌드되었다는 사실을 활용하여 기존 Direct3D 10.1 디바이스를 사용하여 Direct2D API를 호출하는 기능을 제공합니다. 다음 코드 조각은 Direct2D가 디바이스와 연결된 DXGI 1.1 화면에서 Direct3D 10.1 디바이스 렌더링 대상을 가져오는 방법을 보여 줍니다. Direct3D 10.1 디바이스 렌더링 대상은 BeginDraw와 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를 만들면 안 됩니다.
- 최적의 성능을 위해 디바이스와 연결된 텍스처 또는 표면과 같이 항상 하나 이상의 리소스를 유지합니다.
디바이스 공유는 Direct3D 10.1 및 Direct2D 렌더링 API에서 공유하는 하나의 렌더링 디바이스를 프로세스 내 단일 스레드로 사용하는 데 적합합니다. 동기화된 공유 표면을 사용하면 Direct3D 10.1, Direct2D 및 Direct3D 11 API에서 사용하는 여러 렌더링 디바이스의 다중 스레드, In-Process 및 Out-of-process 사용을 사용할 수 있습니다.
Direct3D 10.1 및 Direct2D 상호 운용성의 또 다른 방법은 ID3D1RenderTarget::CreateSharedBitmap을 사용하여 IDXGISurface에서 ID2D1Bitmap 개체를 만드는 것입니다. Direct3D10.1 장면을 비트맵에 쓰고 Direct2D로 렌더링할 수 있습니다. 자세한 내용은 ID2D1RenderTarget::CreateSharedBitmap 메서드를 참조하세요.
Direct2D 소프트웨어 래스터화
Direct2D 소프트웨어 렌더러를 사용하는 경우 Direct3D 10.1과 디바이스 공유는 지원되지 않습니다. 예를 들어 Direct2D 렌더링 대상을 만들 때 D2D1_RENDER_TARGET_USAGE D2D1_RENDER_TARGET_USAGE_FORCE_SOFTWARE_RENDERING 지정합니다.
Direct2D는 WARP10 소프트웨어 래스터라이저를 사용하여 Direct3D 10 또는 Direct3D 11과 디바이스를 공유할 수 있지만 성능은 크게 저하됩니다.
DXGI 1.1 동기화된 공유 표면
Direct3D 11, Direct3D 10.1 및 Direct2D API는 모두 DXGI 1.1을 사용합니다. 이 API는 둘 이상의 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 프로그래밍 가이드를 참조하세요.
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 플래그 집합을 사용하여 호출되는 경우 반환된 인터페이스는 AcquireSync 및 ReleaseSync API를 구현하여 화면에 대한 액세스를 동기화하는 IDXGIKeyedMutex 인터페이스에 대해 쿼리할 수 있습니다. 표면 및 표면을 여는 다른 디바이스(OpenSharedResource 사용)를 만드는 디바이스는 렌더링 명령 전에 IDXGIKeyedMutex::AcquireSync를 호출하고 렌더링이 완료되면 IDXGIKeyedMutex::ReleaseSync를 호출해야 합니다.
WARP 및 REF 디바이스는 공유 리소스를 지원하지 않습니다. WARP 또는 REF 디바이스에서 이 플래그를 사용하여 리소스를 만들려고 하면 create 메서드가 E_OUTOFMEMORY 오류 코드를 반환합니다.
IDXGIKEYEDMUTEX 인터페이스
DXGI 1.1의 새 인터페이스인 IDXGIKeyedMutex는 여러 디바이스에서 사용되는 공유 리소스에 대한 단독 액세스를 허용하는 키 뮤텍스를 나타냅니다. 이 인터페이스와 두 메서드인 AcquireSync 및 ReleaseSync에 대한 참조 설명서는 IDXGIKeyedMutex를 참조하세요.
샘플: 두 Direct3D 10.1 디바이스 간에 동기화된 Surface 공유
아래 예제에서는 두 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;
두 번째 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과 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 상호 운용성의 가장 기본적인 작업은 디바이스 B가 표면에서 핸들을 획득할 때 디바이스 A의 렌더링이 완료되도록 첫 번째 디바이스(디바이스 A)에서 두 번째(디바이스 B)로 단일 표면을 전달하는 것입니다. 따라서 디바이스 B는 걱정 없이 이 표면을 사용할 수 있습니다. 이것은 고전적인 생산자 - 소비자 문제와 매우 유사하며,이 토론은 그런 식으로 문제를 모델로합니다. 표면을 사용한 다음 이를 포기한 첫 번째 디바이스는 생산자(디바이스 A)이며, 처음에 대기 중인 디바이스는 소비자(디바이스 B)입니다. 실제 애플리케이션은 이보다 더 정교하며 여러 생산자-소비자 구성 요소를 연결하여 원하는 기능을 만듭니다.
생산자-소비자 구성 요소는 표면 큐를 사용하여 도우미에서 구현됩니다. 표면은 생산자에 의해 큐에 의해 큐에 추가하고 소비자에 의해 큐에서 해제됩니다. 도우미는 ISurfaceQueue, ISurfaceProducer 및 ISurfaceConsumer의 세 가지 COM 인터페이스를 소개합니다.
도우미의 개략적인 개요
ISurfaceQueue 개체는 공유 표면을 사용하기 위한 구성 요소입니다. 초기화된 Direct3D 디바이스와 고정된 수의 공유 표면을 만드는 설명을 사용하여 만들어집니다. 큐 개체는 리소스 만들기 및 코드 열기를 관리합니다. 표면의 수와 유형이 고정되어 있습니다. 표면이 만들어지면 애플리케이션에서 해당 표면을 추가하거나 제거할 수 없습니다.
ISurfaceQueue 개체의 각 인스턴스는 생산 디바이스에서 소비 디바이스로 표면을 보내는 데 사용할 수 있는 일종의 단방향 거리를 제공합니다. 이러한 여러 단방향 거리를 사용하여 특정 애플리케이션의 디바이스 간에 표면 공유 시나리오를 사용하도록 설정할 수 있습니다.
만들기/개체 수명
큐 개체를 만드는 방법에는 CreateSurfaceQueue를 통해 또는 ISurfaceQueue의 Clone 메서드를 사용하는 두 가지 방법이 있습니다. 인터페이스는 COM 개체이므로 표준 COM 수명 관리가 적용됩니다.
생산자/소비자 모델
큐에 넣기(): 생산자는 이 함수를 호출하여 표면으로 완료되었음을 나타내며, 이제 다른 디바이스에서 사용할 수 있게 됩니다. 이 함수에서 돌아오면 생산자 디바이스는 더 이상 표면에 대한 권한이 없으며 계속 사용하는 것이 안전하지 않습니다.
큐에서 제거(): 사용하는 디바이스는 이 함수를 호출하여 공유 표면을 가져옵니다. API는 큐에서 벗어난 모든 표면을 사용할 준비가 되도록 보장합니다.
메타데이터
API는 메타데이터를 공유 화면과 연결할 수 있습니다.
Enqueue()에는 사용 중인 디바이스에 전달될 추가 메타데이터를 지정하는 옵션이 있습니다. 메타데이터는 생성 시 알려진 최대값보다 작아야 합니다.
Dequeue()는 필요에 따라 버퍼와 포인터를 버퍼 크기에 전달할 수 있습니다. 큐는 해당 큐에 넣기 호출의 메타데이터로 버퍼를 채웁니다.
복제
각 ISurfaceQueue 개체는 단방향 동기화를 해결합니다. 이 API를 사용하는 대부분의 애플리케이션은 닫힌 시스템을 사용한다고 가정합니다. 두 개의 디바이스가 표면을 앞뒤로 보내는 가장 간단한 닫힌 시스템에는 두 개의 큐가 필요합니다. ISurfaceQueue 개체에는 모두 동일한 더 큰 파이프라인의 일부인 여러 큐를 만들 수 있도록 하는 Clone() 메서드가 있습니다.
Clone은 기존 개체에서 새 ISurfaceQueue 개체를 만들고 열려 있는 모든 리소스를 공유합니다. 결과 개체는 원본 큐와 정확히 동일한 표면을 가집니다. 복제된 큐는 서로 다른 메타데이터 크기를 가질 수 있습니다.
표면
ISurfaceQueue는 표면을 만들고 관리하는 역할을 담당합니다. 임의의 표면을 큐에 넣기에는 유효하지 않습니다. 또한 표면에는 하나의 활성 "소유자"만 있어야 합니다. 특정 큐에 있거나 특정 디바이스에서 사용해야 합니다. 여러 큐에 두거나 큐에 넣은 후 디바이스에서 화면을 계속 사용하는 것은 유효하지 않습니다.
API 세부 정보
IsurfaceQueue
큐는 공유 리소스를 만들고 유지 관리합니다. 또한 Clone을 사용하여 여러 큐를 연결하는 기능도 제공합니다. 큐에는 생성 디바이스 및 소비 디바이스를 여는 메서드가 있습니다. 한 번에 하나만 열 수 있습니다.
큐는 다음 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;
멤버
너비, 높이 공유 표면의 크기입니다. 모든 공유 표면의 차원은 같아야 합니다.
형식 공유 화면의 형식입니다. 모든 공유 표면의 형식은 같아야 합니다. 서로 다른 디바이스 쌍이 서로 다른 형식 형식을 공유할 수 있기 때문에 유효한 형식은 사용할 디바이스에 따라 달라집니다.
NumSurfaces 큐의 일부인 표면 수입니다. 고정된 숫자입니다.
MetaDataSize 메타데이터 버퍼의 최대 크기입니다.
플래그 를 지정하여 큐의 동작을 제어합니다. 설명 부분을 참조하세요.
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 반환합니다.
설명
이 함수는 입력 디바이스에 대한 큐의 모든 표면을 열고 캐시합니다. 큐에서 제거에 대한 후속 호출은 단순히 캐시로 이동하며 매번 표면을 다시 열 필요가 없습니다.
IDXGIXSurfaceQueue 복제
typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
UINT MetaDataSize;
DWORD Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;
멤버 MetaDataSize 및 플래그 는 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
이 인터페이스는 앱이 표면을 큐에 넣기를 허용하는 두 가지 메서드를 제공합니다. 표면을 큐에 추가한 후에는 표면 포인터가 더 이상 유효하지 않으며 사용하기에 안전하지 않습니다. 애플리케이션이 포인터를 사용하여 수행해야 하는 유일한 작업은 포인터를 해제하는 것입니다.
메서드 | 설명 |
---|---|
ISurfaceProducer::Enqueue | 큐 개체에 표면을 큐에 추가합니다. 이 호출이 완료되면 생산자가 표면으로 완료되고 표면이 다른 디바이스에 대해 준비됩니다. |
ISurfaceProducer::Flush | 애플리케이션에 비차단 동작이 있어야 하는 경우 사용됩니다. 자세한 내용은 설명을 참조하세요. |
큐
HRESULT Enqueue(
[in] IUnknown *pSurface,
[in] void *pBuffer,
[in] UINT BufferSize,
[in] DWORD Flags
);
매개 변수
pSurface [in]
큐에 추가해야 하는 생산 디바이스의 표면입니다. 이 표면은 동일한 큐 네트워크에서 큐에서 제거된 표면이어야 합니다. pBuffer [in] 메타데이터를 전달하는 데 사용되는 선택적 매개 변수입니다. 큐에서 제거 호출에 전달될 데이터를 가리킵니다.
BufferSize [in] pBuffer의 크기(바이트)입니다.
플래그 [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)는 큐에 넣기 위한 이전 호출에서 DO_NOT_WAIT 플래그를 사용한 경우에만 의미가 있습니다. 그렇지 않으면 no-op이 됩니다. 큐에 넣기 호출이 DO_NOT_WAIT 플래그를 사용하는 경우 큐에 넣기가 즉시 반환되고 GPU-CPU 동기화가 보장되지 않습니다. 표면은 여전히 큐에 넣기된 것으로 간주되며, 생산 디바이스는 계속 사용할 수 없지만 큐에서 제거할 수는 없습니다. 큐에서 제거를 위해 표면을 커밋하려면 Flush를 호출해야 합니다. 현재 큐에 추가된 모든 표면을 커밋하려고 플러시합니다. 플러시에 플래그가 전달되지 않으면 전체 큐를 차단하고 지우고 큐에서 제거를 위해 모든 표면을 준비합니다. DO_NOT_WAIT 플래그를 사용하는 경우 큐는 표면을 확인하여 준비되었는지 확인합니다. 이 단계는 비차단입니다. GPU-CPU 동기화를 완료한 표면은 소비자 디바이스에 대해 준비됩니다. 보류 중인 표면은 영향을 받지 않습니다. 함수는 여전히 플러시해야 하는 표면 수를 반환합니다.
참고 항목
플러시가 큐 의미 체계를 중단하지 않습니다. API는 GPU-CPU 동기화가 발생하는 시기에 관계없이 나중에 큐에 담기 전에 먼저 큐에 포함된 표면이 커밋되도록 보장합니다.
Direct3D 9Ex 및 DXGI Interop 도우미: 사용 방법
대부분의 사용 사례에는 여러 표면을 공유하는 두 디바이스가 포함됩니다. 가장 간단한 시나리오이기 때문에 이 문서에서는 API를 사용하여 이 목표를 달성하는 방법을 자세히 설명하고, 비차단 변형에 대해 설명하고, 세 개의 디바이스를 초기화하는 방법에 대한 간략한 섹션으로 끝납니다.
두 디바이스
이 도우미를 사용하는 예제 애플리케이션은 Direct3D 9Ex 및 Direct3D 11을 함께 사용할 수 있습니다. 애플리케이션은 두 디바이스에서 콘텐츠를 처리하고 Direct3D 9를 사용하여 콘텐츠를 표시할 수 있습니다. 처리는 콘텐츠 렌더링, 비디오 디코딩, 컴퓨팅 셰이더 실행 등을 의미할 수 있습니다. 모든 프레임에 대해 애플리케이션은 먼저 Direct3D 11로 처리한 다음 Direct3D 9로 처리하고 마지막으로 Direct3D 9와 함께 제공됩니다. 또한 Direct3D 11을 사용하여 처리하면 Direct3D 9에서 사용해야 하는 일부 메타데이터가 생성됩니다. 이 섹션에서는 초기화, 주 루프 및 정리 시퀀스에 해당하는 세 부분으로 구성된 도우미 사용에 대해 설명합니다.
초기화
초기화에는 다음 단계가 포함됩니다.
- 두 디바이스를 모두 초기화합니다.
- 루트 큐 만들기: m_11to9Queue.
- 루트 큐에서 복제: m_9to11Queue.
- 두 큐에서 OpenProducer/OpenConsumer를 호출합니다.
큐 이름은 숫자 9와 11을 사용하여 생산자인 API와 소비자를 나타냅니다. m_ 소비자큐에대한 생성자입니다. 따라서 m_11to9Queue Direct3D 11 디바이스가 Direct3D 9 디바이스에서 사용하는 표면을 생성하는 큐를 나타냅니다. 마찬가지로 m_9to11Queue Direct3D 9가 Direct3D 11에서 사용하는 표면을 생성하는 큐를 나타냅니다.
루트 큐는 처음에 가득 차고 복제된 모든 큐는 처음에 비어 있습니다. 큐에 넣기 및 큐에 넣기의 첫 번째 주기와 메타데이터의 가용성을 제외하고는 애플리케이션에 문제가 되지 않습니다. 큐에서 메타데이터를 요청하지만 설정되지 않은 경우(처음에는 아무것도 없거나 큐에 아무것도 설정하지 않았기 때문에) 큐에서 메타데이터가 수신되지 않은 것을 볼 수 있습니다.
두 디바이스를 모두 초기화합니다.
m_pD3D9Device = InitializeD3D9ExDevice(); m_pD3D11Device = InitializeD3D11Device();
루트 큐를 만듭니다.
이 단계에서는 표면도 만듭니다. 크기 및 형식 제한은 공유 리소스를 만드는 것과 동일합니다. 메타데이터 버퍼의 크기는 생성 시 고정되며, 이 경우 UINT만 전달합니다.
고정된 수의 표면을 사용하여 큐를 만들어야 합니다. 성능은 시나리오에 따라 달라집니다. 표면이 여러 대 있으면 디바이스가 사용 중일 가능성이 높아집니다. 예를 들어 표면이 하나만 있는 경우 두 디바이스 사이에 병렬 처리가 없습니다. 반면, 표면 수를 늘리면 메모리 공간이 증가하여 성능이 저하됩니다. 이 예제에서는 두 개의 표면을 사용합니다.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);
생산자 및 소비자 디바이스를 엽니다.
애플리케이션은 큐에 넣기 및 큐에서 제거를 호출하기 전에 이 단계를 수행해야 합니다. 생산자 및 소비자를 열면 큐에 넣기/큐에서 제거 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를 사용하고 큐에 추가할 플래그를 사용하지 않습니다. 비차단 방식으로 도우미를 사용하려면 몇 가지 변경만 하면 됩니다. 이 섹션에서는 한 스레드의 두 디바이스에서 비차단 사용을 보여줍니다.
초기화
초기화는 플래그를 제외하고 동일합니다. 애플리케이션은 단일 스레드이므로 생성에 해당 플래그를 사용합니다. 이로 인해 일부 동기화 코드가 해제되어 성능이 향상될 수 있습니다.
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개의 큐가 있습니다. 표면은 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);
앞에서 설명한 것처럼 복제는 복제되는 큐에 관계없이 동일한 방식으로 작동합니다. 예를 들어 두 번째 복제 호출이 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 샘플 앱에서 사용할 수 있는 동기화 메커니즘과 같이 상호 운용할 수 있습니다.