DXGI 概觀

Microsoft DirectX Graphics Infrastructure (DXGI) 辨識圖形的某些部分會比其他部分更慢。 DXGI 的主要目標是管理可與 DirectX 圖形執行時間無關的低階工作。 DXGI 提供未來圖形元件的通用架構;利用 DXGI 的第一個元件是 Microsoft Direct3D 10。

在舊版 Direct3D 中,直接3D 執行時間包含低階工作,例如列舉硬體裝置、將轉譯的畫面格呈現至輸出、控制 Gamma 和管理全螢幕轉換。 這些工作現在會在 DXGI 中實作。

DXGI 的目的是要與核心模式驅動程式和系統硬體通訊,如下圖所示。

應用程式、dxgi 和驅動程式與硬體之間通訊的圖表

應用程式可以直接存取 DXGI,或在 D3D11_1.h、D3D11.h、D3D10_1.h 或 D3D10.h 中呼叫 Direct3D API,以為您處理 DXGI 的通訊。 如果您的應用程式需要列舉裝置或控制資料呈現至輸出的方式,您可以直接使用 DXGI。

本主題包含下列各節。

若要查看 Direct3D 11 硬體支援的格式:

列舉配接器

配接器是電腦硬體和軟體功能的抽象概念。 您的電腦上通常有許多介面卡。 有些裝置是在硬體 (實作,例如您的視訊卡) ,有些則實作在軟體 (,例如 Direct3D 參考轉譯器) 。 配接器會實作圖形應用程式所使用的功能。 下圖顯示具有單一電腦的系統、兩張介面卡 () 和三個輸出監視器。

具有兩張視訊卡和三部監視器的電腦圖表

列舉這些硬體片段時,DXGI 會為每個輸出 (或監視) 建立 IDXGIOutput1 介面,以及每個視訊卡的 IDXGIAdapter2 介面, (即使它是內建在主機板) 中的視訊卡也一樣。 列舉是使用IDXGIFactory 介面呼叫 IDXGIFactory::EnumAdapters來傳回一組代表視訊硬體的IDXGIAdapter介面。

DXGI 1.1 新增 了 IDXGIFactory1 介面。 IDXGIFactory1::EnumAdapters1 會傳回一組代表視訊硬體的 IDXGIAdapter1 介面。

如果您想要在使用 Direct3D API 時選取特定的視訊硬體功能,建議您反復呼叫 D3D11CreateDeviceD3D11CreateDeviceAndSwapChain 函式搭配每個介面卡控制碼和可能的硬體 功能層級。 如果指定的配接器支援功能層級,此函式就會成功。

列舉Windows 8配接器的新資訊

從Windows 8開始,一律會出現名為 「Microsoft Basic Render Driver」 的介面卡。 此介面卡的 VendorId 為 0x1414 ,以及 0x8c的 DeviceID。 此配接器也會在其DXGI_ADAPTER_DESC2結構的Flags成員中設定DXGI_ADAPTER_FLAG_SOFTWARE值。 此配接器是沒有顯示輸出的僅限轉譯裝置。 DXGI 永遠不會傳回此介面卡 的DXGI_ERROR_DEVICE_REMOVED

如果電腦的顯示驅動程式無法運作或停用,電腦的主要 (Null) 介面卡也可能稱為「Microsoft Basic Render Driver」。但此配接器具有輸出,且未設定 DXGI_ADAPTER_FLAG_SOFTWARE 值。 作業系統和應用程式預設會使用此配接器。 如果已安裝或啟用顯示器驅動程式,應用程式可以接收此介面卡 的DXGI_ERROR_DEVICE_REMOVED ,然後必須重新列舉介面卡。

當電腦的主要顯示配接器是 「Microsoft Basic Display Adapter」 (WARP 配接器) 時,該電腦也有第二張介面卡。 第二張介面卡是沒有顯示輸出且 DXGI 永遠不會傳回 DXGI_ERROR_DEVICE_REMOVED的轉譯裝置。

如果您想要使用 WARP 進行轉譯、計算或其他長時間執行的工作,建議您使用僅限轉譯的裝置。 您可以呼叫 IDXGIFactory1::EnumAdapters1 方法,以取得僅限轉譯裝置的指標。 當您在D3D11CreateDeviceDriverType參數中指定D3D_DRIVER_TYPE_WARP時,也會建立僅轉譯裝置,因為 WARP 裝置也會使用僅限轉譯的 WARP 配接器。

簡報

您的應用程式工作是轉譯畫面,並要求 DXGI 將這些畫面呈現至輸出。 如果應用程式有兩個可用的緩衝區,它可以轉譯一個緩衝區,同時呈現另一個緩衝區。 應用程式可能需要兩個以上的緩衝區,視轉譯畫面的時間或簡報所需的畫面播放速率而定。 建立的緩衝區集稱為交換鏈結,如下所示。

交換鏈結的圖例

交換鏈結有一個前端緩衝區和一或多個後端緩衝區。 每個應用程式都會建立自己的交換鏈結。 為了將資料呈現至輸出的速度最大化,交換鏈結幾乎一律會在顯示子系統的記憶體中建立,如下圖所示。

顯示子系統的圖例

顯示器子系統 (通常是視訊卡,但可以在主機板上實作,) 包含 GPU、數位轉類比轉換器 (DAC) 和記憶體。 交換鏈結會配置在此記憶體中,讓呈現速度非常快。 顯示子系統會將前端緩衝區中的資料呈現至輸出。

交換鏈結設定為以全螢幕或視窗模式繪製,這不需要知道輸出是視窗化還是全螢幕。 全螢幕模式交換鏈結可以藉由切換顯示器解析度來優化效能。

建立交換鏈結

Direct3D 9 與 Direct3D 10 之間的差異:Direct3D 10 是使用 DXGI 的第一個圖形元件。 DXGI 有一些不同的交換鏈結行為。
  • 在 DXGI 中,交換鏈結會在建立交換鏈結時系結至視窗。 這項變更可改善效能並節省記憶體。 舊版 Direct3D 允許交換鏈結變更交換鏈結所系結的視窗。
  • 在 DXGI 中,交換鏈結會在建立時系結至轉譯裝置。 Direct3D 建立裝置函式所傳回的裝置物件會實作 IUnknown 介面。 您可以呼叫 QueryInterface 來查詢裝置的對應 IDXGIDevice2 介面。 轉譯裝置的變更需要重新建立交換鏈結。
  • 在 DXGI 中,可用的交換效果DXGI_SWAP_EFFECT_DISCARD和DXGI_SWAP_EFFECT_SEQUENTIAL。 從Windows 8 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL交換效果也可供使用。 下表顯示 Direct3D 9 與 DXGI 交換效果定義的對應。

    D3D9 交換效果 DXGI 交換效果
    D3DSWAPEFFECT_DISCARD DXGI_SWAP_EFFECT_DISCARD
    D3DSWAPEFFECT_COPY 具有 1 個緩衝區的DXGI_SWAP_EFFECT_SEQUENTIAL
    D3DSWAPEFFECT_FLIP DXGI_SWAP_EFFECT_SEQUENTIAL具有 2 個以上的緩衝區
    D3DSWAPEFFECT_FLIPEX DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 2 個以上的緩衝區

交換鏈結的緩衝區是以特定大小和特定格式建立。 應用程式會 (指定這些值,或者您可以在啟動時從目標視窗繼承大小) ,然後選擇性地修改這些值,因為視窗大小變更以回應使用者輸入或程式事件。

建立交換鏈結之後,您通常會想要將影像轉譯成它。 以下是一個程式碼片段,可設定 Direct3D 內容以轉譯成交換鏈結。 此程式碼會從交換鏈結擷取緩衝區、從該緩衝區建立轉譯目標檢視,然後在裝置上設定它:

ID3D11Resource * pBB;
ThrowFailure( pSwapChain->GetBuffer(0, __uuidof(pBB),    
  reinterpret_cast<void**>(&pBB)), "Couldn't get back buffer");
ID3D11RenderTargetView * pView;
ThrowFailure( pD3D11Device->CreateRenderTargetView(pBB, NULL, &pView), 
  "Couldn't create view" );
pD3D11DeviceContext->OMSetRenderTargets(1, &pView, 0);
        

應用程式將框架轉譯成交換鏈結緩衝區之後,請呼叫 IDXGISwapChain1::P resent1。 然後,應用程式就可以轉譯下一個影像。

交換鏈結的小心和饋送

轉譯影像之後,請呼叫 IDXGISwapChain1::P resent1 並轉譯下一個影像。 這就是您的責任範圍。

如果您先前稱為 IDXGIFactory::MakeWindowAssociation,使用者可以按Alt-Enter鍵組合,DXGI 會在視窗模式和全螢幕模式之間轉換您的應用程式。 建議使用 IDXGIFactory::MakeWindowAssociation,因為強烈建議使用者的標準控制機制。

雖然您不需要撰寫比已描述更多的程式碼,但幾個簡單的步驟可讓應用程式更具回應性。 最重要的考慮是調整交換鏈結緩衝區的大小,以回應輸出視窗的大小調整。 當然,應用程式的最佳路由是回應WM_SIZE,並呼叫 IDXGISwapChain::ResizeBuffers,傳遞訊息參數中包含的大小。 當應用程式拖曳視窗的框線時,此行為明顯可讓應用程式對使用者做出良好的回應,但它也是啟用順暢全螢幕轉換的功能。 每當發生這類轉換時,您的視窗就會收到WM_SIZE訊息,而呼叫 IDXGISwapChain::ResizeBuffers 是交換鏈結有機會重新配置緩衝區的儲存體,以獲得最佳呈現。 這就是為什麼在呼叫 IDXGISwapChain::ResizeBuffers之前,應用程式必須釋放現有緩衝區上具有的任何參考。

無法呼叫 IDXGISwapChain::ResizeBuffers 以回應切換至全螢幕模式, (最自然地回應WM_SIZE) ,可能會排除翻轉的優化,其中 DXGI 可以直接交換顯示的緩衝區,而不是複製全螢幕的資料。

IDXGISwapChain1::P resent1 會通知您,如果您的輸出視窗完全透過 DXGI_STATUS_OCCLUDED遮蔽。 發生這種情況時,建議您讓應用程式進入待命模式 (,方法是呼叫 IDXGISwapChain1::P resent1 搭配 DXGI_PRESENT_TEST) ,因為用來轉譯畫面的資源會浪費。 使用 DXGI_PRESENT_TEST 可防止任何資料呈現,同時仍執行遮蔽檢查。 一旦 IDXGISwapChain1::P resent1 傳回S_OK,您應該結束待命模式;請勿使用傳回碼切換至待命模式,因為這麼做會使交換鏈結無法放棄全螢幕模式。

Direct3D 11.1 執行時間可從 Windows 8 開始提供翻轉模型交換鏈結 (,也就是在swapEffect成員中設定DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL值DXGI_SWAP_CHAIN_DESC或DXGI_SWAP_CHAIN_DESC1) 當您將框架呈現至具有翻轉模型交換鏈結的輸出時,DXGI 會將來自所有管線狀態位置的背景緩衝區解除系結,例如寫入回緩衝區 0 的輸出合併轉譯目標。 因此,建議您在轉譯回後端緩衝區之前,立即呼叫 ID3D11DeviceCoNtext::OMSetRenderTargets 。 例如,請勿呼叫 OMSetRenderTargets ,然後執行計算著色器工作,最終不會轉譯至資源。 如需翻轉模型交換鏈結及其優點的詳細資訊,請參閱 DXGI 翻轉模型

注意

在 Direct3D 10 和 Direct3D 11 中,您不需要呼叫 IDXGISwapChain::GetBuffer ,在呼叫 IDXGISwapChain1::P resent1 之後擷取備份緩衝區 0,因為為了方便起見,後端緩衝區的身分識別會變更。 這不會在 Direct3D 12 中發生,而且您的應用程式必須改為手動追蹤緩衝區索引。

處理視窗大小調整

您可以使用 IDXGISwapChain::ResizeBuffers 方法來處理視窗調整大小。 呼叫 ResizeBuffers之前,您必須釋放交換鏈結緩衝區的所有未完成參考。 通常保存交換鏈結緩衝區參考的物件是轉譯目標檢視。

下列範例程式碼示範如何從 WindowProc 處理常式內呼叫 ResizeBuffers ,以取得WM_SIZE訊息:

    case WM_SIZE:
        if (g_pSwapChain)
        {
            g_pd3dDeviceContext->OMSetRenderTargets(0, 0, 0);

            // Release all outstanding references to the swap chain's buffers.
            g_pRenderTargetView->Release();

            HRESULT hr;
            // Preserve the existing buffer count and format.
            // Automatically choose the width and height to match the client rect for HWNDs.
            hr = g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
                                            
            // Perform error handling here!

            // Get buffer and create a render-target-view.
            ID3D11Texture2D* pBuffer;
            hr = g_pSwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D),
                                         (void**) &pBuffer );
            // Perform error handling here!

            hr = g_pd3dDevice->CreateRenderTargetView(pBuffer, NULL,
                                                     &g_pRenderTargetView);
            // Perform error handling here!
            pBuffer->Release();

            g_pd3dDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL );

            // Set up the viewport.
            D3D11_VIEWPORT vp;
            vp.Width = width;
            vp.Height = height;
            vp.MinDepth = 0.0f;
            vp.MaxDepth = 1.0f;
            vp.TopLeftX = 0;
            vp.TopLeftY = 0;
            g_pd3dDeviceContext->RSSetViewports( 1, &vp );
        }
        return 1;

選擇 DXGI 輸出和大小

根據預設,DXGI 會選擇包含視窗大部分工作區的輸出。 這是 DXGI 在回應 alt-enter 時,唯一可供 DXGI 使用的選項。 如果應用程式自行選擇移至全螢幕模式,則可以呼叫 IDXGISwapChain::SetFullscreenState ,並傳遞明確的 IDXGIOutput1 (或 Null,如果應用程式很滿意讓 DXGI 決定) 。

若要在全螢幕或視窗化時調整輸出大小,建議您呼叫 IDXGISwapChain::ResizeTarget,因為此方法也會調整目標視窗的大小。 由於目標視窗已調整大小,因此作業系統會傳送 WM_SIZE,而您的程式碼會自然呼叫 IDXGISwapChain::ResizeBuffers 以回應。 因此,需要浪費心力來調整緩衝區大小,然後調整目標大小。

全螢幕模式偵錯

只有在絕對需要時,DXGI 交換鏈結才會全螢幕模式。 這表示您可以使用多個監視器對全螢幕應用程式進行偵錯,只要偵錯視窗不會與交換鏈結的目標視窗重迭即可。 或者,您可以藉由不設定 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 旗標來防止模式完全切換。

如果允許模式切換,每當另一個視窗遮蔽其輸出視窗時,交換鏈結就會放棄全螢幕模式。 遮蔽檢查會在IDXGISwapChain1::P resent1期間執行,或由目的為watch的個別執行緒執行,以查看應用程式是否沒有回應 (,且不再呼叫IDXGISwapChain1::P resent1) 。 若要停用個別執行緒造成參數的能力,請將下列登錄機碼設定為任何非零值。

HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog

終結交換鏈結

您無法以全螢幕模式釋放交換鏈結,因為這麼做可能會建立執行緒爭用 (,這會導致 DXGI 引發非持續性的例外狀況) 。 在釋放交換鏈結之前,請先使用 IDXGISwapChain::SetFullscreenState ( ( FALSENull ) ) 切換至視窗模式,然後呼叫 IUnknown::Release

使用旋轉監視器

應用程式不需要擔心監視方向,DXGI 會在簡報期間視需要旋轉交換鏈結緩衝區。 當然,這個額外的輪替可能會影響效能。 若要獲得最佳效能,請執行下列動作來處理應用程式中的旋轉:

  • 使用 DXGI_SWAP_CHAIN_FLAG_NONPREROTATED。 這會透過改變其投影矩陣) ,通知 DXGI 應用程式將產生旋轉的影像 (。 請注意,此旗標只有在全螢幕模式時才有效。
  • 在其旋轉大小中配置每個交換鏈結緩衝區。 如有需要,請使用 IDXGIOutput::GetDesc 來取得這些值。

藉由在應用程式中執行旋轉,DXGI 只會執行複本,而不是複製和旋轉。

從 Windows 8 開始提供的 Direct3D 11.1 執行時間提供翻轉模型交換鏈結 (,也就是在DXGI_SWAP_CHAIN_DESC1) SwapEffect成員中設定DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL值的交換鏈結。 若要將翻轉模型交換鏈結提供的簡報優化最大化,建議您讓應用程式將內容導向,以符合內容在內容完全佔用輸出時所在的特定輸出。 如需翻轉模型交換鏈結及其優點的詳細資訊,請參閱 DXGI 翻轉模型

切換模式

DXGI 交換鏈結可能會在進行全螢幕轉換時變更輸出的顯示模式。 若要啟用自動顯示模式變更,您必須在交換鏈結描述中指定 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 。 如果顯示模式自動變更,DXGI 會選擇最適中的模式, (大小和解析度不會變更,但色彩深度可能會) 。 調整交換鏈結緩衝區的大小不會造成模式切換。 交換鏈結會隱含承諾,如果您選擇完全符合目標輸出所支援顯示模式的背景緩衝區,則會在該輸出上進入全螢幕模式時切換至該顯示模式。 因此,您可以選擇背景緩衝區大小和格式來選擇顯示模式。

全螢幕效能提示

當您在全螢幕應用程式中呼叫 IDXGISwapChain1::P resent1 時,交換鏈結會翻轉 (,而不是將後端緩衝區的內容) 到前端緩衝區。 這需要使用列舉顯示模式建立交換鏈結, (在 DXGI_SWAP_CHAIN_DESC1) 中指定的。 如果您無法列舉顯示模式,或在描述中不正確地指定顯示模式,交換鏈結可能會改為執行位區塊傳輸 (bitblt) 。 bitblt 會造成額外的延展複製,以及一些增加的視訊記憶體使用量,而且很難偵測。 若要避免這個問題,請列舉顯示模式,並在建立交換鏈結之前正確初始化交換鏈結描述。 這可確保在全螢幕模式中翻轉時的效能上限,並避免額外的記憶體負荷。

多執行緒考慮

當您在具有多個執行緒的應用程式中使用 DXGI 時,必須小心避免建立死結,其中兩個不同的執行緒正在等待彼此完成。 發生這種情況有兩種情況。

  • 轉譯執行緒不是訊息幫浦執行緒。
  • 執行 DXGI API 的執行緒與建立視窗的執行緒不同。

當您使用全螢幕交換鏈結時,請務必在轉譯執行緒上等候訊息幫浦執行緒。 例如,從轉譯執行緒呼叫 IDXGISwapChain1::P resent1 ( ,) 可能會導致轉譯執行緒等候訊息幫浦執行緒。 發生模式變更時,如果 Present1 呼叫 ::SetWindowPos () 或 ::SetWindowStyle () ,而且其中一個方法呼叫 ::SendMessage () ,就可能發生此案例。 在此案例中,如果訊息幫浦執行緒有一個保護它的重要區段,或轉譯執行緒遭到封鎖,則這兩個執行緒將會死結。

如需搭配多個執行緒使用 DXGI 的詳細資訊,請參閱 多執行緒和 DXGI

DLLMain 的 DXGI 回應

由於 DllMain 函式無法保證載入和卸載 DLL 的順序,因此建議您應用程式的 DllMain 函式不會呼叫 Direct3D 或 DXGI 函式或方法,包括建立或釋放物件的函式或方法。 如果您的應用程式的 DllMain 函式呼叫特定元件,該元件可能會呼叫作業系統上不存在的另一個 DLL,這會導致作業系統損毀。 Direct3D 和 DXGI 可能會載入一組 DLL,通常是一組與電腦不同的驅動程式。 因此,即使您的應用程式在開發和測試電腦上未在 DllMain 函式呼叫 Direct3D 或 DXGI 函式或方法時當機,它可能會在另一部電腦上執行時損毀。

為了防止建立可能導致作業系統損毀的應用程式,DXGI 會在指定的情況下提供下列回應:

  • 如果您的應用程式的 DllMain 函式釋放其 DXGI 處理站的最後一個參考,DXGI 會引發例外狀況。
  • 如果您的應用程式的 DllMain 函式建立 DXGI 處理站,DXGI 會傳回錯誤碼。

DXGI 1.1 變更

我們已在 DXGI 1.1 中新增下列功能。

DXGI 1.2 變更

我們已在 DXGI 1.2 中新增下列功能。

  • 立體交換鏈結
  • 翻轉模型交換鏈結
  • 優化簡報 (捲動、已變更矩形和旋轉)
  • 改善共用資源和同步處理
  • 桌面重複
  • 優化視訊記憶體的使用
  • 每個圖元支援 16 位 (bpp) 格式 (DXGI_FORMAT_B5G6R5_UNORM、DXGI_FORMAT_B5G5R5A1_UNORM、DXGI_FORMAT_B4G4R4A4_UNORM)
  • 偵錯 API

如需 DXGI 1.2 的詳細資訊,請參閱 DXGI 1.2 改善

DXGI 的程式設計指南