共用方式為


Windows 圖形 API 之間的 Surface 共用

本主題提供使用 Windows 圖形 API 之間介面共用的互操作性技術概觀,包括 Direct3D 11、Direct2D、DirectWrite、Direct3D 10 和 Direct3D 9Ex。 如果您已經具備這些 API 的工作知識,本檔可協助您使用多個 API,在針對 Windows 7 或 Windows Vista 作業系統所設計的應用程式中,將多個 API 轉譯成相同的介面。 本主題也提供其他資源的最佳做法指導方針和指標。

注意

針對 DirectX 11.1 執行時間上的 Direct2D 和 DirectWrite 互操作性,您可以使用 Direct2D 裝置和裝置內容 直接轉譯至 Direct3D 11 裝置。

 

本主題包含下列各節。

簡介

在本檔中,Windows 圖形 API 互操作性是指不同 API 共用相同的轉譯介面。 這種互操作性可讓應用程式利用多個 Windows 圖形 API 來建立吸引人的顯示器,並藉由維持與現有 API 的相容性,輕鬆移轉至新技術。

在 Windows 7 中,圖形轉譯 API 是 Direct3D 11、Direct2D、Direct3D 10.1、Direct3D 10.0、Direct3D 9Ex、Direct3D 9Ex、Direct3D 9c 和舊版 Direct3D API,以及 GDI 和 GDI+。 Windows 映射元件 (WIC) 和 DirectWrite 是影像處理的相關技術,Direct2D 會執行文字轉譯。 DirectX 影片加速 API (DXVA),以 Direct3D 9c 和 Direct3D 9Ex 為基礎,用於視訊處理。

隨著 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 都可以利用 DirectX 圖形基礎結構 (DXGI) 1.1,這可讓同步處理的共用介面完全支持這些 API 之間的互操作性。 DXGI 1.1 型 API 會透過從 DXGI 1.1 介面取得 GDI 裝置內容,與 GDI+ 相互操作,並與 GDI+ 相互關聯。

Direct3D 9Ex 運行時間支援未同步處理的表面共用。 DXVA 型視訊應用程式可以使用 Direct3D 9Ex 和 DXGI 互操作性協助程式,來使用 Direct3D 9Ex 型 DXVA 與 Direct3D 11 的計算著色器互操作性,也可以與 Direct2D 進行 2D 控件或文字轉譯互通。 WIC 和 DirectWrite 也會與其他 Direct3D API 相互操作 GDI、Direct2D 和關聯。

Direct3D 10.0、Direct3D 9c 和較舊的 Direct3D 運行時間不支持共用介面。 系統記憶體復本將繼續用於與 GDI 或 DXGI 型 API 的互操作性。

請注意,本檔中的互操作性案例是指多個圖形 API 轉譯至共用轉譯介面,而不是相同的應用程式視窗。 針對以不同表面為目標的個別 API 同步處理,然後複合到相同視窗,則不在本文的範圍之外。

API 互操作性概觀

Windows 圖形 API 的 Surface 共用互操作性可以說明 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 之間的互操作性支援。

Windows 圖形 API 之間的互操作性支持圖表

在此圖表中,箭號會顯示可透過連線 API 存取相同介面的互操作性案例。 藍色箭號表示 Windows Vista 中引進的互操作性機制。 綠色箭號表示新 API 的互操作性支援,或協助較舊的 API 與較新的 API 互操作的改進。 例如,綠色箭號代表裝置共用、同步處理的共用介面支援、Direct3D 9Ex/DXGI 同步處理協助程式,以及從相容的表面取得 GDI 裝置內容。

互操作性案例

從 Windows 7 和 Windows Vista 7IP 開始,Windows 圖形 API 的主要供應項目支援將多個 API 轉譯至相同的 DXGI 1.1 介面。

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 裝置呼叫 Direct2D API,並利用 Direct2D 建置在 Direct3D 10.1 和 DXGI 1.1 運行時間之上的事實。 下列代碼段說明 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 格式。 該裝置是由呼叫 D3D10CreateDevice1 搭配參數D3D10_CREATE_DEVICE_BGRA_SUPPORT所建立。 從 Direct3D 10 功能層級 9.1 開始,支援 BGRA 格式。
  • 應用程式不應該建立多個與相同 Direct3D10.1 裝置建立關聯的 ID2D1RenderTargets。
  • 為了獲得最佳效能,請隨時保留至少一個資源,例如與裝置相關聯的紋理或表面。

裝置共用適用於 Direct3D 10.1 和 Direct2D 轉譯 API 共用之一個轉譯裝置的同進程單個線程使用量。 同步處理的共用介面可啟用 Direct3D 10.1、Direct2D 和 Direct3D 11 API 所使用的多個轉譯裝置的多線程、同進程和跨進程使用。

Direct3D 10.1 和 Direct2D 互操作性的另一種方法是使用 ID3D1RenderTarget::CreateSharedBitmap,這會從 IDXGISurface 建立 ID2D1Bitmap 物件。 您可以將 Direct3D10.1 場景寫入位圖,並使用 Direct2D 轉譯它。 如需詳細資訊,請參閱 ID2D1RenderTarget::CreateSharedBitmap方法

Direct2D 軟體點陣化

使用 Direct3D 軟體轉譯器時,不支援使用 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,它提供兩個或多個 Direct3D 裝置同步讀取和寫入至相同視訊記憶體介面的功能。DXGISurface1。 使用同步共用介面的轉譯裝置可以是 Direct3D 10.1 或 Direct3D 11 裝置,每個裝置都是在相同進程或跨進程中執行。

應用程式可以使用同步共用表面,從 Direct2D 轉譯目標物件取得 Direct3D 10.1 和 Direct3D 10.1 之間的任何 DXGI 1.11 和 Direct3D 10.1 裝置之間的互通。

在 Direct3D 10.1 和更新版本中,若要使用 DXGI 1.1,請確定 Direct3D 裝置是使用 DXGI 1.1 配接器物件建立的,該對像是從 DXGI 1.1 Factory 物件列舉而來。 呼叫 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 進行同步處理。 下列資源建立 Direct3D 10.1 API,全都採用 D3D10_RESOURCE_MISC_FLAG 參數,以支援新的旗標。

  • 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 裝置上使用此旗標建立資源,會導致 create 方法傳回E_OUTOFMEMORY錯誤碼。
IDXGIKEYEDMUTEX 介面
DXGI 1.1 中的新介面 IDXGIKeyedMutex 代表索引鍵 Mutex,允許獨佔存取多個裝置所使用的共用資源。 如需此介面及其兩種方法 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 使用同步處理的共用 Surface

同樣地,若要在 Direct3D 11 與 Direct3D 10.1 API 之間共用,可以從 API 裝置建立同步共用介面,並與其他 API 裝置共用或移出進程。

使用 Direct2D 的應用程式可以共用 Direct3D 10.1 裝置,並使用同步處理的共用介面來與 Direct3D 11 或其他 Direct3D 10.1 裝置互操作,無論它們屬於相同的進程或不同的進程。 不過,對於單一進程、單個線程應用程式,裝置共用是 Direct2D 與 Direct3D 10 或 Direct3D 11 之間互操作性的最高高效能和有效方法。

軟體轉譯器

當應用程式使用 Direct3D 或 Direct2D 軟體轉譯器時,不支援同步共用表面,包括參考轉譯器和 WARP,而不是使用圖形硬體加速。

Direct3D 9Ex 與 DXGI 型 API 之間的互操作性

Direct3D 9Ex API 包含介面共用的概念,可讓其他 API 從共用介面讀取。 若要共用對 Direct3D 9Ex 共用介面的讀取和寫入,您必須將手動同步處理新增至應用程式本身。

Direct3D 9Ex 共用介面加上手動同步處理協助程式

Direct3D 9Ex 和 Direct3D 10 或 11 互操作性中最基本的工作是將單一表面從第一個裝置 (裝置 A) 傳遞至第二個 (裝置 B),如此一來,當裝置 B 取得表面上的句柄時,裝置 A 的轉譯保證已完成。 因此,裝置 B 可以使用此表面而不需擔心。 這與傳統生產者-消費者問題非常類似,而此討論會以這種方式建立問題模型。 第一個使用表面的裝置,然後放棄它是生產者(裝置 A),而一開始等待的裝置是取用者(裝置 B)。 任何真實世界應用程式都比這更複雜,並將多個生產者-取用者建置組塊鏈結在一起,以建立所需的功能。

產生者-取用者建置組塊是使用介面佇列在協助程序中實作。 表面會由產生者加入佇列,並由取用者清除佇列。 協助程序引進三個 COM 介面:ISurfaceQueue、ISurfaceProducer 和 ISurfaceConsumer。

協助程式的高階概觀

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 物件 (“root” 佇列)。
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。

備註

此函式會開啟輸入裝置佇列中的所有介面,並加以快取。 後續對 Dequeue 的呼叫只會移至快取,而不需要每次重新開啟介面。

複製IDXGIXSurfaceQueue

typedef struct SHARED_SURFACE_QUEUE_CLONE_DESC {
  UINT         MetaDataSize;
  DWORD        Flags;
} SHARED_SURFACE_QUEUE_CLONE_DESC;

成員 MetaDataSizeFlags 的行為與 CreateSurfaceQueue 的行為相同。

HRESULT Clone(
  [in]   SHARED_SURFACE_QUEUE_CLONE_DESC *pDesc,
  [out]  IDXGIXSurfaceQueue **ppQueue
);

參數

pDesc [in] 結構,提供要建立之 Clone 物件的描述。 此參數應該初始化。
ppQueue [out] 傳回初始化的物件。

備註

您可以從任何現有的佇列物件複製,即使它不是根目錄也一樣。

IDXGIXSurfaceConsumer

HRESULT Dequeue(
  [in]      REFIID    id,
  [out]     void      **ppSurface,
  [in,out]  void      *pBuffer,
  [in,out]  UINT      *pBufferSize,
  [in]      DWORD     dwTimeout
);

參數
id [in]
取用裝置 2D 表面的 REFIID。

  • 針對IDirect3DDevice9,REFIID應__uuidof(IDirect3DTexture9)。
  • 針對ID3D10Device,REFIID應__uuidof(ID3D10Texture2D)。
  • 針對ID3D11Device,REFIID應__uuidof(ID3D11Texture2D)。

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 參數的運作方式與 Windows 同步處理 API 相同,例如 WaitForSingleObject。 針對非封鎖行為,請使用逾時 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。 請參閱 Flush 的。 如果未傳遞旗標 (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(),因此應用程式不需要這麼做。 呼叫加入佇列之後,應用程式必須釋放介面,因為它們不再使用它。

清除

HRESULT Flush(
  [in]  DWORD Flags,
  [out] UINT *nSurfaces
);

參數
旗標 [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 會向應用程式指出已完成的工作,以及是否要執行任何工作。

備註

只有在先前呼叫加入佇列時,才有意義地使用 DO_NOT_WAIT 旗標;否則,它將會是無作業。 如果對加入佇列的呼叫使用DO_NOT_WAIT旗標,則加入佇列會立即傳回,而且不保證 GPU-CPU 同步處理。 表面仍被視為加入佇列,生產裝置無法繼續使用,但無法用於清除佇列。 若要嘗試認可清除佇列的表面,必須呼叫 Flush。 Flush 嘗試認可目前加入佇列的所有表面。 如果未將旗標傳遞至 Flush,則會封鎖並清除整個佇列,讓其中的所有表面都準備好清除佇列。 如果使用DO_NOT_WAIT旗標,佇列會檢查介面,以查看其中是否有任何已就緒;此步驟為非封鎖。 已完成 GPU-CPU 同步處理的介面將會準備好供取用者裝置使用。 仍在擱置的介面將不會受到影響。 函式會傳回仍然需要排清的介面數目。

注意

Flush 不會中斷佇列語意。 API 保證介面會先加入佇列,再於稍後加入佇列表面之前認可,而不論 GPU-CPU 同步何時發生。

 

Direct3D 9Ex 和 DXGI Interop 協助程式:如何使用

我們預期大部分的使用案例都牽涉到兩個共享數個表面的裝置。 由於這也是最簡單的案例,本文會詳細說明如何使用 API 來達成此目標、討論非封鎖變化,最後還有關於初始化三個裝置的簡短章節。

兩個裝置

使用此協助程式的應用程式範例可以同時使用 Direct3D 9Ex 和 Direct3D 11。 應用程式可以使用這兩個裝置處理內容,並使用 Direct3D 9 來呈現內容。 處理可能表示轉譯內容、譯碼視訊、執行計算著色器等等。 針對每個畫面,應用程式會先使用 Direct3D 11 處理,然後使用 Direct3D 9 處理,最後會呈現 Direct3D 9。 此外,使用 Direct3D 11 的處理會產生 Direct3D 9 存在需要取用的一些元數據。 本節涵蓋對應至此順序之三個部分的協助程式使用方式:初始化、主要迴圈和清除。

初始化
初始化牽涉到下列步驟:

  1. 初始化這兩個裝置。
  2. 建立根佇列:m_11to9Queue。
  3. 從根佇列複製:m_9to11Queue。
  4. 在兩個佇列上呼叫 OpenProducer/OpenConsumer。

佇列名稱會使用數位 9 和 11 來指出哪個 API 是生產者,而哪個是取用者:m_產生者傳送至取用佇列。 因此,m_11to9Queue指出 Direct3D 11 裝置產生 Direct3D 9 裝置取用表面的佇列。 同樣地,m_9to11Queue表示 Direct3D 9 產生 Direct3D 11 取用表面的佇列。
根佇列一開始已滿,且所有複製的佇列一開始都是空的。 除了 Enqueues 和 Dequeues 的第一個週期,以及元數據的可用性之外,應用程式不應該有問題。 如果清除佇列要求元數據但未設定任何元數據(可能是因為一開始沒有任何專案,或加入佇列未設定任何專案),清除佇列就會看到未收到任何元數據。

  1. 初始化這兩個裝置。

    m_pD3D9Device = InitializeD3D9ExDevice();
    m_pD3D11Device = InitializeD3D11Device();
    
  2. 建立根佇列。
    此步驟也會建立介面。 大小和格式限制與建立任何共享資源相同。 元數據緩衝區的大小會在建立時修正,在此情況下,我們只會傳遞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);
    
  3. 複製根佇列。
    每個複製的佇列都必須使用相同的介面,但可以有不同的元數據緩衝區大小和不同的旗標。 在此情況下,沒有從 Direct3D 9 到 Direct3D 11 的元數據。

    SURFACE_QUEUE_CLONE_DESC Desc;
    Desc.MetaDataSize = 0;
    Desc.Flags        = 0;
    
    m_11to9Queue->Clone(&Desc, &m_9to11Queue);
    
  4. 開啟生產者和取用者裝置。
    應用程式必須先執行此步驟,才能呼叫 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 進行逾時,而沒有旗標加入佇列。 如果您想要以非封鎖方式使用協助程式,只需要進行一些變更。 本節顯示一個線程上這兩個裝置的非封鎖使用。

初始化
初始化與旗標除外。 因為應用程式是單個線程,因此請使用該旗標來建立。 這會關閉某些同步處理程序代碼,這可能會改善效能。

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);
}

更複雜的解決方案可以檢查加入佇列和排清的傳回值,以判斷是否需要排清。

三個裝置

擴充先前的範例以涵蓋多個裝置很簡單。 下列程式代碼會執行初始化。 建立產生者/取用者對象之後,使用它們的程式代碼會相同。 此範例有三個裝置,因此有三個佇列。 Surface 會從 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);

如先前所述,無論複製哪個佇列,複製的運作方式都相同。 例如,第二個 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。 此運行時間可在新開發的 API 內啟用同步處理表面共享支援,例如 Direct3D 11、Direct3D 10.1 和 Direct2D。 新 API 與現有 API 之間的互操作性改善可協助應用程式移轉和回溯相容性。 Direct3D 9Ex 和 DXGI 1.1 取用者 API 可以互通,如舊版 Win32 範例應用程式中提供的同步處理機制所示,這些應用程式可在封 存的 MSDN 程式代碼庫Microsoft範例 存放庫中找到。