DirectX 圖形基礎結構 (DXGI) :最佳做法

Microsoft DirectX Graphics Infrastructure (DXGI) 是新的子系統,其在封裝部分 Direct3D 10、10.1、11 和 11.1 所需低層級工作的 Windows Vista 中引進, 從 Direct3D 9 程式設計人員的觀點來看,DXGI 包含大部分的程式碼,可用來列舉、交換鏈結建立,以及先前封裝成 Direct3D 9 API 的簡報。 當您將應用程式移植到 DXGI 和 Direct3D 10.x 和 Direct3D 11.x 時,您必須考慮一些考慮,以確保程式順利執行。

本文討論重要的移植問題。

Full-Screen問題

從 Direct3D 9 移植到 DXGI 和 Direct3D 10.x 或 Direct3D 11.x 時,從視窗模式移至全螢幕模式的相關問題,通常可能會導致開發人員感到麻煩。 主要問題是因為 Direct3D 9 應用程式與 DXGI 應用程式不同,需要更實際操作的方式來追蹤視窗樣式和視窗狀態。 當模式變更程式碼移植到 DXGI 上執行時,它通常會造成非預期的行為。

Direct3D 9 應用程式通常會藉由設定前端緩衝區的解析度,強制裝置進入全螢幕獨佔模式,然後將背景緩衝區解析度設定為相符,以處理轉換成全螢幕模式。 個別路徑用於視窗大小的變更,因為每當應用程式收到WM_SIZE訊息時,都必須從視窗進程管理這些路徑。

DXGI 會藉由結合這兩個案例來嘗試簡化此方法。 例如,當視窗框線以視窗模式拖曳時,應用程式會收到WM_SIZE訊息。 DXGI 會攔截此訊息,並自動調整前端緩衝區的大小。 應用程式只需要呼叫 IDXGISwapChain::ResizeBuffers ,將背景緩衝區大小調整為WM_SIZE中當做參數傳遞的大小。 同樣地,當應用程式需要在全螢幕和視窗模式之間切換時,應用程式可以直接呼叫 IDXGISwapChain::SetFullscreenState。 DXGI 會調整前端緩衝區的大小,以符合新選取的全螢幕模式,並將WM_SIZE訊息傳送至應用程式。 應用程式會再次呼叫 ResizeBuffers,就像拖曳視窗框線一樣。

上述說明的方法遵循非常特定的路徑。 DXGI 預設會將全螢幕解析度設定為桌面解析度。 不過,許多應用程式會切換為慣用的全螢幕解析度。 在這種情況下,DXGI 會提供 IDXGISwapChain::ResizeTarget。 呼叫 SetFullscreenState之前,應該先呼叫此專案。 雖然您可以先以相反順序呼叫這些方法, (SetFullscreenState ,後面接著 ResizeTarget) ,這樣做會導致將額外的WM_SIZE訊息傳送至應用程式。 (這樣做也可能造成閃爍,因為 DXGI 可能會強制執行兩種模式變更。) 呼叫SetFullscreenState之後,建議您再次呼叫ResizeTarget,並將DXGI_MODE_DESCRefreshRate成員零出。這相當於 DXGI 中的無作業指示,但可以避免重新整理速率的問題,接下來會加以討論。

在全螢幕模式中,會停用桌面視窗管理員 (DWM) 。 DXGI 可以執行翻轉來呈現背景緩衝區內容,而不是執行 blit,它會在視窗模式中執行。 不過,如果不符合特定需求,則可以復原此效能提升。 為了確保 DXGI 會執行翻轉,而不是 blit,前端緩衝區和背景緩衝區的大小必須相同。 如果應用程式正確地處理其WM_SIZE訊息,則不應該發生問題。 此外,格式必須相同。

大部分應用程式的問題都是重新整理率。 呼叫 ResizeTarget 中指定的重新整理速率必須是交換鏈結所使用的 IDXGIOutput 物件所列舉的重新整理速率。 如果應用程式舍去傳遞至ResizeTargetDXGI_MODE_DESCRefreshRate成員,DXGI 可以自動計算此值。 請務必不要假設一律支援某些重新整理率,而且只要將值硬式編碼即可。 通常,開發人員選擇 60 Hz 作為重新整理速率,而不知道來自監視器的列舉重新整理速率大約是來自監視器的 60,000 / 1,001 Hz。 如果重新整理速率不符合預期的 60 次重新整理率,DXGI 會強制以全螢幕模式執行 blit,而不是翻轉。

開發人員經常遇到的最後一個問題是如何變更全螢幕解析度,同時維持全螢幕模式。 呼叫 ResizeTargetSetFullscreenState 有時會成功,但全螢幕解析度仍是桌面解析度。 此外,開發人員可以建立全螢幕交換鏈結並提供特定解析度,只尋找 DXGI 預設為桌面解析度,而不論傳入的數位為何。 除非另有指示,否則 DXGI 預設為全螢幕交換鏈結的桌面解析度。 建立全螢幕交換鏈結時,DXGI_SWAP_CHAIN_DESC結構的Flags成員必須設定為DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,才能覆寫 DXGI 的預設行為。 此旗標也可以傳遞至 ResizeTarget 以動態啟用或停用此功能。

多個監視器

搭配多個監視器使用 DXGI 時,有兩個規則要遵循。

第一個規則適用于在多個監視器上建立兩個或多個全螢幕交換鏈結。 建立這類交換鏈結時,最好將所有交換鏈結建立為視窗,然後將它們設定為全螢幕。 如果交換鏈結是以全螢幕模式建立,則建立第二個交換鏈結會導致模式變更傳送至第一個交換鏈結,這可能會導致全螢幕模式終止。

第二個規則會套用至輸出。 監看建立交換鏈結時所使用的輸出。 使用 DXGI, IDXGIOutput 物件可控制當變成全螢幕時,交換鏈結所使用的監視。 不同于 DXGI,Direct3D 9 沒有輸出的概念。

視窗樣式和 DXGI

在全螢幕模式與視窗模式之間切換時,Direct3D 9 應用程式有許多工作要做。 這項工作大部分都涉及變更視窗樣式來新增和移除框線、新增捲軸等等。 當應用程式移植到 DXGI 和 Direct3D 10.x 或 Direct3D 11.x 時,此程式碼通常就位。 根據所做的變更,在模式之間切換可能會導致非預期的行為。 例如,切換至視窗模式時,即使有特別設定這些樣式的程式碼,應用程式可能不再有視窗框架或視窗框線。 這是因為 DXGI 現在會自行處理此樣式的大部分變更。 手動設定視窗樣式可能會干擾 DXGI,這可能會導致非預期的行為。

建議的行為是盡可能少執行工作,並讓 DXGI 處理與視窗的大部分互動。 不過,如果應用程式需要處理自己的視窗化行為,可以使用 IDXGIFactory::MakeWindowAssociation 來指示 DXGI 停用部分自動視窗處理。

多執行緒和 DXGI

在多執行緒應用程式中使用 DXGI以確保不會發生死結,要特別注意。 由於 DXGI 與視窗的密切互動,因此偶爾會將視窗訊息傳送至相關聯的應用程式視窗。 DXGI 需要視窗變更才能繼續執行,因此它會使用 SendMessage,這是同步呼叫。 應用程式必須在 SendMessage 傳回之前處理視窗訊息。

在 DXGI 呼叫和訊息幫浦位於相同執行緒 (或單一執行緒應用程式) 的應用程式中,只需要完成一些工作。 當 DXGI 呼叫與訊息幫浦位於相同的執行緒上時, SendMessage 會呼叫視窗的 WindowProc。 這會略過訊息幫浦,並允許在呼叫 SendMessage之後繼續執行。 請記住, IDXGISwapChain 呼叫,例如 IDXGISwapChain::P resent,也會被視為 DXGI 呼叫;DXGI 可能會延遲 ResizeBuffersResizeTarget 的工作,直到呼叫 Present 為止。

如果 DXGI 呼叫和訊息幫浦位於不同的執行緒上,則必須小心以避免死結。 當訊息幫浦和 SendMessage 位於不同的執行緒時, SendMessage 會將訊息新增至視窗的訊息佇列,並等候視窗處理該訊息。 如果視窗程式忙碌或訊息幫浦未呼叫,訊息可能永遠不會處理,DXGI 會無限期等候。

例如,如果應用程式在一個執行緒上具有其訊息幫浦,並在另一個執行緒上呈現,它可能會想要變更模式。 訊息幫浦執行緒會告知轉譯執行緒變更模式,並等候模式變更完成。 不過,轉譯執行緒會呼叫 DXGI 函式,接著呼叫 SendMessage,這會封鎖訊息幫浦處理訊息為止。 發生死結的原因是這兩個執行緒現在都會遭到封鎖,而且正在彼此等候。 若要避免這種情況,請勿封鎖訊息幫浦。 如果區塊無法避免,則所有 DXGI 互動都應該發生在與訊息幫浦相同的執行緒上。

Gamma 和 DXGI

雖然 Gamma 在 Direct3D 10.x 或 Direct3D 11.x 中使用 SRGB 紋理處理得最好,但 Gamma 坡形仍然適用于想要與 2.2 不同的 Gamma 值,或是使用不支援 SRGB 的轉譯目標格式的開發人員。 請注意在透過 DXGI 設定 Gamma 坡形時發生兩個問題。 第一個問題是傳入 IDXGIOutput::SetGammaControl 的坡形值是浮點值,而不是 WORD 值。 此外,請確定從 Direct3D 9 移植的程式碼不會先嘗試轉換成 WORD 值,再將這些值傳遞給 SetGammaControl

第二個問題是,變更為全螢幕模式之後, SetGammaControl 可能無法運作,視所使用的 IDXGIOutput 物件而定。 變更為全螢幕模式時,DXGI 會建立新的輸出物件,並使用 物件進行輸出上的所有後續作業。 如果在全螢幕模式切換之前列舉的輸出上呼叫 SetGammaControl ,則呼叫不會導向至 DXGI 目前使用的輸出。 若要避免這種情況,請呼叫 IDXGISwapChain::GetContainingOutput 以取得目前的輸出,然後呼叫 SetGammaControl 關閉此輸出以取得正確的行為。

如需使用 gamma 修正的相關資訊,請參閱 使用 gamma 修正

DXGI 1.1

Windows 7 中包含的 Direct3D 11 執行時間,並安裝到 Windows Vista 包含 DXGI 1.1 版。 此更新會新增許多新格式的定義, (特別是 BGRA、10 位 X2 偏差,以及 Direct3D 11 的 BC6H 和 BC7 紋理壓縮) ,以及用於列舉遠端桌面連線的新版本 DXGI Factory 和配接器介面 (CreateDXGIFactory1IDXGIFactory1IDXGIAdapter1) 。

當您使用 Direct3D 11 時,執行時間預設會在呼叫 D3D11CreateDeviceD3D11CreateDeviceAndSwapChain 搭配 Null IDXGIAdapter 指標時使用 DXGI 1.1。 不支援在相同程式中混合使用 DXGI 1.0 和 DXGI 1.1。 不支援在相同進程中混合來自不同處理站的 DXGI 物件實例。 因此,當您使用 DirectX 11 時,任何明確使用 DXGI 介面都會使用 「DXGI.DLL」 中CreateDXGIFactory1進入點所建立的IDXGIFactory1,以確保應用程式一律使用 DXGI 1.1。

DXGI 1.2

Windows 8中包含的 Direct3D 11.1 執行時間也包含 DXGI 1.2 版。

DXGI 1.2 啟用這些功能:

  • 身歷聲轉譯

  • 每圖元 16 位格式

    • 現在完全支援DXGI_FORMAT_B5G6R5_UNORM和DXGI_FORMAT_B5G5R5A1_UNORM
    • 已新增新的DXGI_FORMAT_B5G5R5A1_UNORM格式
  • 視訊格式

  • 新的 DXGI 介面

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