Share via


Windows 圖形 API 之間的 Surface 共用

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

注意

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

 

本主題包含下列各節。

簡介

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

在 Windows 7 (和 Windows Vista SP2 搭配 Windows 7 Interop Pack、Vista 7IP) 中,圖形轉譯 API 是 Direct3D 11、Direct2D、Direct3D 10.1、Direct3D 10.0、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 Graphics Infrastructure (DXGI) 1.1,讓同步處理的共用介面完全支援這些 API 之間的互通性。 DXGI 1.1 型 API 會透過從 DXGI 1.1 表面取得 GDI 裝置內容,與 GDI+ 相互關聯,與 GDI+ 互通。 如需詳細資訊,請參閱 MSDN 上提供的 DXGI 和 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 互通性概觀

您可以在 API 對 API 案例和對應的互通性功能中描述 Windows 圖形 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 (及其相關 API,例如 DirectWrite 和 WIC) 可以使用 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 格式。
  • 應用程式不應該建立多個 ID2D1RenderTargets,而與相同的 Direct3D10.1 裝置建立關聯。
  • 為了獲得最佳效能,請隨時保留至少一個資源,例如與裝置相關聯的紋理或表面。

裝置共用適用于 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 軟體點陣化

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

應用程式可以使用已同步處理的共用表面,從 Direct2D 轉譯目標物件取得 Direct3D 10.1 和 Direct3D 10.1 之間的任何 DXGI 1.1 和 Direct3D 11 和 Direct3D 10.1 裝置之間的交互操作。

在 Direct3D 10.1 和更新版本的 API 中,若要使用 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,這些 API 全都採用 D3D10_RESOURCE_MISC_FLAG 參數,以支援新的旗標。

  • ID3D10Device1::CreateTexture1D
  • ID3D10Device1::CreateTexture2D
  • ID3D10Device1::CreateTexture3D
  • ID3D10Device1::CreateBuffer

如果任何列出的函式都使用設定D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX旗標來呼叫,則可以查詢傳回的介面是否有 IDXGIKeyedMutex 介面,其會實作 AcquireSync 和 ReleaseSync API 以同步存取介面。 在介面的任何轉譯命令完成轉譯之前,必須先使用 OpenSharedResource) 來建立表面和任何其他開啟表面 (的裝置,以及 IDXGIKeyedMutex::ReleaseSync 呼叫 IDXGIKeyedMutex::ReleaseSync。
WARP 和 REF 裝置不支援共用資源。 嘗試在 WARP 或 REF 裝置上使用這個旗標建立資源,會導致 create 方法傳回E_OUTOFMEMORY錯誤碼。
IDXGIKEYEDMUTEX 介面
DXGI 1.1 IDXGIKeyedMutex 中的新介面代表索引鍵 Mutex,允許獨佔存取多個裝置所使用的共用資源。 如需此介面及其兩種方法 AcquireSync 和 ReleaseSync 的參考檔,請參閱 IDXGIKeyedMutex

範例:兩個 Direct3D 10.1 裝置之間的同步處理介面共用

下列範例說明在兩個 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 之間互通性的最高效能且有效率的方法。

軟體點陣化程式

當應用程式使用 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) 。 任何真實世界應用程式都比這更複雜,並將多個產生者-取用者建置組塊鏈結在一起,以建立所需的功能。

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

High-Level協助程式概觀

ISurfaceQueue 物件是使用共用表面的建置組塊。 它會使用初始化的 Direct3D 裝置和描述來建立固定數目的共用表面。 佇列物件會管理資源的建立和程式碼開啟。 表面的數目和類型是固定的;建立介面之後,應用程式就無法新增或移除它們。

ISurfaceQueue 物件的每個實例都提供一種單向街道,可用來將表面從產生裝置傳送到取用裝置。 多個這類單向街道可用來啟用特定應用程式裝置之間的表面共用案例。

建立/物件存留期
有兩種方式可以建立佇列物件:透過 CreateSurfaceQueue,或透過 ISurfaceQueue 的 Clone 方法。 因為介面是 COM 物件,所以會套用標準 COM 存留期管理。
生產者/取用者模型
排入佇列 () :產生者會呼叫此函式,以指出它已透過表面完成,現在可供另一部裝置使用。 從此函式傳回時,產生者裝置就不再具有表面的許可權,而且會不安全地繼續使用它。
Dequeue () :取用的裝置會呼叫此函式以取得共用表面。 API 保證任何已清除佇列的介面都已準備好可供使用。
中繼資料
API 支援將中繼資料與共享介面產生關聯。
Enqueue () 可以選擇指定將傳遞至取用裝置的其他中繼資料。 中繼資料在建立時必須小於已知的最大值。
Dequeue () 可以選擇性地傳遞緩衝區和緩衝區大小的指標。 佇列會填入緩衝區中對應的排入佇列呼叫中的中繼資料。
複製
每個 ISurfaceQueue 物件都會解決單向同步處理。 我們假設大部分使用此 API 的應用程式都會使用封閉式系統。 有兩個裝置來回傳送表面的最簡單封閉系統需要兩個佇列。 ISurfaceQueue 物件具有 Clone () 方法,可讓您建立多個屬於相同較大管線一部分的佇列。
Clone 會從現有的物件建立新的 ISurfaceQueue 物件,並在它們之間共用所有已開啟的資源。 產生的物件與來源佇列完全相同。 複製的佇列可以彼此有不同的中繼資料大小。
表面
ISurfaceQueue 負責建立和管理其表面。 將任意表面排入佇列無效。 此外,表面應該只有一個作用中的「擁有者」。它應該位於特定佇列上,或由特定裝置使用。 在多個佇列上,或讓裝置在排入佇列之後繼續使用表面並無效。

API 詳細資料

IsurfaceQueue

佇列負責建立和維護共用資源。 它也提供使用 Clone 鏈結多個佇列的功能。 佇列有開啟產生裝置和取用裝置的方法。 隨時只能開啟其中一個。

佇列會公開下列 API:

API 描述
CreateSurfaceQueue (「root」 佇列) 建立 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。

備註

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

複製識別碼XGIXSurfaceQueue

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 設為 NullpBufferSize 設定為 0,且傳回值為 0x80070120 (WIN32_TO_HRESULT (WAIT_TIMEOUT) ) 。

備註

如果佇列是空的,此 API 可以封鎖。 dwTimeout參數的運作方式與 Windows 同步處理 API 相同,例如 WaitForSingleObject。 針對非封鎖行為,請使用 0 的逾時。

ISurfaceProducer

此介面提供兩種方法,可讓應用程式排入佇列表面。 加入介面佇列之後,表面指標就不再有效,而且無法使用。 應用程式應該使用指標執行的唯一動作是釋放它。

方法 Description
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
);

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

注意

Flush 不會中斷佇列語意。 不論 GPU-CPU 同步何時發生,API 保證在介面排入佇列之前,會先認可介面排入佇列。

 

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 取用表面的佇列。
根佇列一開始已滿,所有複製的佇列一開始都是空的。 除了排入佇列和清除佇列的第一個週期,以及中繼資料的可用性之外,這不應該是應用程式的問題。 如果清除佇列要求中繼資料,但未設定任何中繼資料, (因為一開始沒有任何專案,或排入佇列未設定任何) ,則清除佇列會看到未收到任何中繼資料。

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

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

三個裝置

擴充先前的範例以涵蓋多個裝置很簡單。 下列程式碼會執行初始化。 建立產生者/取用者物件之後,要使用的程式碼會相同。 此範例有三個裝置,因此有三個佇列。 表面會從 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 可以交互操作,如 MSDN 程式碼庫上的範例協助程式程式碼所提供的同步處理機制所示。