Microsoft DirectX 圖形基礎結構 (DXGI) 辨識圖形的某些部分比其他部分發展更慢。 DXGI 的主要目標是管理可獨立於 DirectX 圖形執行階段的低階工作。 DXGI 為未來的圖形元件提供通用架構;第一個利用 DXGI 的元件是 Microsoft Direct3D 10。
在舊版的 Direct3D 中,Direct3D 執行階段中包含列舉硬體裝置、將轉譯的影像呈現於輸出、控制伽馬值,以及管理全螢幕轉換等低階工作。 這些工作現在會在 DXGI 中實作。
DXGI 的目的是與核心模式驅動程式和系統硬體通訊,如下圖所示。
應用程式可以直接存取 DXGI,或呼叫 D3D11_1.h、D3D11.h、D3D10_1.h 或 D3D10.h 中的 Direct3D API,以為您處理與 DXGI 的通訊。 如果您的應用程式需要列舉裝置或控制數據呈現給輸出的方式,您可能想要直接使用 DXGI。
本主題包含下列各節。
若要查看 Direct3D 11 硬體支援哪些格式:
- Direct3D 功能層級 12.1 硬體的 DXGI 格式支援
- Direct3D 功能層級 12.0 硬體的 DXGI 格式支援
- Direct3D 功能層級 11.1 硬體的 DXGI 格式支援
- Direct3D 功能層級 11.0 硬體的 DXGI 格式支援
- Direct3D 10Level9 格式的硬體支援
- Direct3D 10.1 格式的硬體支援
- Direct3D 10 格式的硬體支援
列舉適配器
適配器是電腦硬體和軟體功能的抽象概念。 您的機器上通常有很多適配器。 有些裝置是在硬體中實作 (,例如視訊卡) ,有些裝置是在軟體中實作 (,例如 Direct3D 參考點陣器) 。 配接器會實作圖形應用程式所使用的功能。 下圖顯示具有單一電腦、兩個配接卡 (視訊卡) 和三個輸出監視器的系統。
列舉這些硬體時,DXGI 會為每個輸出 (或監視器) 建立 IDXGIOutput1 介面,並為每個視訊卡建立 IDXGIAdapter2 介面 (即使它是內建於主機板) 的視訊卡也一樣。 列舉是使用 IDXGIFactory 介面呼叫 IDXGIFactory::EnumAdapters 來傳回一組代表視訊硬體的 IDXGIAdapter 介面來完成。
DXGI 1.1 已新增 IDXGIFactory1 介面。 IDXGIFactory1::EnumAdapters1 會傳回一組代表視訊硬體的 IDXGIAdapter1 介面。
如果您想要在使用 Direct3D API 時選取特定的視訊硬體功能,建議您反覆呼叫 D3D11CreateDevice 或 D3D11CreateDeviceAndSwapChain 函式,並搭配每個配接器控制碼和可能的硬體 功能層級。 如果指定的配接器支援功能層級,則此函式會成功。
關於列舉 Windows 8 配接器的新資訊
從 Windows 8 開始,一個名為「Microsoft 基本轉譯驅動程式」的轉接器始終存在。 此配接器的 VendorId 為 0x1414,DeviceID 為 0x8c。 此配接器也在其 DXGI_ADAPTER_DESC2 結構的 Flags 成員中設定了 DXGI_ADAPTER_FLAG_SOFTWARE 值。 此配接器是沒有顯示輸出的純轉譯裝置。 DXGI 永遠不會傳回此配接器的 DXGI_ERROR_DEVICE_REMOVED 。
如果電腦的顯示驅動程式無法運作或已停用,則電腦的主要 (NULL) 配接器也可能稱為「Microsoft 基本轉譯驅動程式」。但此轉接器有輸出,且未設定 DXGI_ADAPTER_FLAG_SOFTWARE 值。 作業系統和應用程式預設使用此轉接器。 如果已安裝或啟用顯示驅動程式,應用程式可能會收到此配接器的DXGI_ERROR_DEVICE_REMOVED,然後必須重新列舉配接器。
當電腦的主要顯示介面卡是「Microsoft 基本顯示介面卡」(WARP 介面卡)時,該電腦也有第二個介面卡。 第二個配接器是僅供渲染用途且沒有顯示輸出的裝置,DXGI 永遠不會傳回 DXGI_ERROR_DEVICE_REMOVED。
如果您想要將 WARP 用於轉譯、運算或其他長時間執行的工作,建議您使用僅轉譯裝置。 您可以呼叫 IDXGIFactory1::EnumAdapters1 方法,以取得僅限轉譯裝置的指標。 當您在 D3D11CreateDevice 的 DriverType 參數中指定 D3D_DRIVER_TYPE_WARP 時,您也會建立僅轉譯裝置,因為 WARP 裝置也會使用僅轉譯 WARP 配接器。
Presentation
應用程式的工作是轉譯畫面,並要求 DXGI 將這些畫面呈現給輸出。 如果應用程式有兩個可用的緩衝區,它可以轉譯一個緩衝區,同時呈現另一個緩衝區。 應用程式可能需要兩個以上的緩衝區,視轉譯畫面所需的時間或所需的畫面播放速率而定。 建立的緩衝區集稱為交換鏈,如下所示。
交換鏈結有一個前端緩衝區和一或多個後端緩衝區。 每個應用程式都會建立自己的交換鏈。 為了將資料呈現至輸出的速度最大化,幾乎一律會在顯示子系統的記憶體中建立交換鏈結,如下圖所示。
顯示子系統(通常是視頻卡,但可以在主板上實現)包含一個 GPU、一個數字模擬轉換器 (DAC) 和內存。 交換鏈分配在此內存中,以使演示非常快。 顯示子系統將前緩衝區中的資料呈現給輸出。
交換鏈結會設定為以全螢幕或視窗模式繪製,這就不需要知道輸出是視窗化還是全螢幕。 全螢幕模式交換鏈可以透過切換顯示解析度來優化效能。
建立交換鏈
Direct3D 9 和 Direct3D 10 之間的差異:Direct3D 10 是第一個使用 DXGI 的圖形元件。 DXGI 有一些不同的交換鏈結行為。
|
交換鏈的緩衝區會以特定大小和特定格式建立。 應用程式會在啟動時指定這些值 (,或者您可以從目標視窗繼承大小) ,然後可以選擇性地修改這些值,因為視窗大小會變更以回應使用者輸入或程式事件。
建立交換鏈之後,您通常會想要將影像呈現在其中。 以下是設定 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::Present1。 然後,應用程式可以轉譯下一個影像。
交換鏈的維護和管理
轉譯影像之後,請呼叫 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::Present1 會通知您的輸出視窗是否已被 DXGI_STATUS_OCCLUDED 完全遮擋。 發生這種情況時,建議您的應用程式進入待命模式 (以 DXGI_PRESENT_TEST 呼叫 IDXGISwapChain1::P resent1) ,因為用來轉譯畫面的資源會浪費。 使用 DXGI_PRESENT_TEST 將防止在執行遮擋檢查時顯示任何資料。 一旦 IDXGISwapChain1::Present1 傳回 S_OK,您應該結束待命模式,請勿使用返回碼來切換至待命模式,因為這樣做可能會讓交換鏈無法放棄全螢幕模式。
Direct3D 11.1 執行階段自 Windows 8 開始提供翻轉模式交換鏈,亦即在 DXGI_SWAP_CHAIN_DESC 或 DXGI_SWAP_CHAIN_DESC1 的 SwapEffect 成員中設定 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 值的交換鏈。 當您將畫面呈現給具有翻轉模型交換鏈結的輸出時,DXGI 會從寫入後置緩衝區 0 的所有管線狀態位置取消系結,例如輸出合併轉譯目標。 因此,建議您在轉譯至後端緩衝區之前立即呼叫 ID3D11DeviceContext::OMSetRenderTargets 。 例如,請勿呼叫 OMSetRenderTargets ,然後執行最終不會轉譯至資源的計算著色器工作。 如需翻轉模型交換鏈結及其優點的詳細資訊,請參閱 DXGI 翻轉模型。
備註
在 Direct3D 10 和 Direct3D 11 中,呼叫 IDXGISwapChain1::P resent1 之後,您不需要呼叫 IDXGISwapChain::GetBuffer 來擷取回溯緩衝區 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 指令後,進入全螢幕模式時唯一可用的選項。 如果應用程式選擇自行移至全螢幕模式,則可以呼叫 IDXGISwapChain::SetFullscreenState 並傳遞明確的 IDXGIOutput1 (或 Null,如果應用程式願意讓 DXGI 決定) 。
若要在全螢幕或視窗時調整輸出大小,建議您呼叫 IDXGISwapChain::ResizeTarget,因為這個方法也會調整目標視窗的大小。 由於目標視窗已調整大小,因此作業系統會傳送 WM_SIZE,而您的程式碼自然會呼叫 IDXGISwapChain::ResizeBuffers 來回應。 因此,調整緩衝區大小,然後調整目標大小是浪費精力。
在全螢幕模式下除錯
DXGI 交換鏈只有在絕對必要時才會放棄全螢幕模式。 這表示您可以使用多個監視器來偵錯全螢幕應用程式,只要偵錯視窗不與交換鏈結的目標視窗重疊即可。 或者,您可以透過不設定 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 旗標來完全防止模式切換。
如果允許模式切換,每當交換鏈結的輸出視窗被另一個視窗遮擋時,交換鏈結就會放棄全螢幕模式。 遮擋檢查是在 IDXGISwapChain1::P resent1 期間執行,或由個別執行程執行,其目的是監看應用程式是否沒有回應 (,而且不再呼叫 IDXGISwapChain1::P resent1) 。 若要停用個別執行緒導致切換的能力,請將下列登錄機碼設定為任何非零值。
HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog
銷毀交換鏈
您不得在全螢幕模式下釋放交換鏈,因為這樣可能會造成執行緒爭用,導致 DXGI 引發不可持續的例外狀況。 在釋放交換鏈結之前,請先切換至視窗模式 (使用 IDXGISwapChain::SetFullscreenState( FALSE,NULL )) ,然後呼叫 IUnknown::Release。
使用旋轉顯示器
應用程式不需要擔心監視器方向,視需要,DXGI 會在簡報期間輪替交換鏈結緩衝區。 當然,這種額外的輪換會影響性能。 為了獲得最佳效能,請執行下列動作來處理應用程式中的輪換:
- 使用 DXGI_SWAP_CHAIN_FLAG_NONPREROTATED。 這會通知 DXGI 應用程式會產生旋轉的影像 (例如,藉由變更其投影矩陣) 。 需要注意的一件事是,此標誌僅在全屏模式下有效。
- 以其旋轉大小配置每個交換鏈結緩衝區。 如有必要,請使用 IDXGIOutput::GetDesc 來取得這些值。
藉由在應用程式中執行旋轉,DXGI 只需執行複製,而不是同時執行複製和旋轉。
Direct3D 11.1 運行時間從 Windows 8 開始提供翻轉模型交換鏈結 (也就是,在 DXGI_SWAP_CHAIN_DESC1 的 SwapEffect 成員中設定DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL值的交換鏈結) 。 為了最大化翻轉模型交換鏈結所提供的簡報優化,我們建議您讓應用程式在內容完全佔據輸出時,將內容的方向調整為與其所在的特定輸出相匹配。 如需翻轉模型交換鏈結及其優點的詳細資訊,請參閱 DXGI 翻轉模型。
切換模式
DXGI 交換鏈結可能會在進行全螢幕轉換時變更輸出的顯示模式。 若要啟用自動顯示模式變更,您必須在交換鏈描述中指定 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 。 如果顯示模式自動變更,DXGI 會選擇最溫和的模式 (大小和解析度不會變更,但色彩深度可能會變更) 。 調整交換鏈結緩衝區的大小不會導致模式切換。 交換鏈結會隱含承諾,如果您選擇完全符合目標輸出所支援的顯示模式的後端緩衝區,則會在該輸出上進入全螢幕模式時切換至該顯示模式。 因此,您可以透過選擇反向緩衝區大小和格式來選擇顯示模式。
全螢幕效能提示
當您在全螢幕應用程式上呼叫 IDXGISwapChain1::P resent1 時,交換鏈結會將後端緩衝區的內容翻轉 (,而不是 blits) 至正面緩衝區。 這需要使用列舉顯示模式 (在 DXGI_SWAP_CHAIN_DESC1 中指定) 來建立交換鏈結。 如果您無法列舉顯示模式,或在描述中不正確地指定顯示模式,交換鏈結可能會改為執行位塊傳輸 (bitblt) 。 bitblt 會導致額外的拉伸副本,同時增加顯示記憶體的使用量,而且很難被偵測到。 若要避免此問題,請列舉顯示模式,並在建立交換鏈結之前正確初始化交換鏈結描述。 這將確保在全螢幕模式下翻轉時獲得最佳效能,並避免額外的記憶體開銷。
多執行緒考量
當您在具有多個執行緒的應用程式中使用 DXGI 時,您必須小心避免建立死結,其中兩個不同的執行緒會互相等候完成。 有兩種情況可能會發生這種情況。
- 轉譯執行緒不是訊息泵執行緒。
- 執行 DXGI API 的執行緒與建立視窗的執行緒不同。
請注意,當您使用全螢幕交換鏈時,不要讓訊息循環執行緒等待轉譯執行緒。 例如,從渲染執行緒呼叫 IDXGISwapChain1::Present1 可能會導致渲染執行緒等待消息幫浦執行緒。 發生模式變更時,如果 Present1 呼叫 ::SetWindowPos() 或 ::SetWindowStyle() ,且其中任一方法呼叫 ::SendMessage(),則可能會發生此案例。 在此案例中,如果訊息幫浦執行緒有保護它的重要區段,或轉譯執行線遭到封鎖,則這兩個執行序列將會死結。
如需搭配多個執行緒使用 DXGI 的詳細資訊,請參閱 多執行緒和 DXGI。
來自 DLLMain 的 DXGI 回應
由於 DllMain 函式無法保證載入和卸載 DLL 的順序,因此建議您應用程式的 DllMain 函式不要呼叫 Direct3D 或 DXGI 函式或方法,包括建立或釋放物件的函式或方法。 如果應用程式的 DllMain 函式呼叫特定元件,該元件可能會呼叫作業系統上不存在的另一個 DLL,這會導致作業系統當機。 Direct3D 和 DXGI 可能會載入一組 DLL,通常是一組驅動程式,因電腦而異。 因此,即使您的應用程式在其 DllMain 函式呼叫 Direct3D 或 DXGI 函式或方法時不會在開發和測試電腦上當機,在另一部電腦上執行時也可能會當機。
為了防止您建立可能導致作業系統當機的應用程式,DXGI 會在指定的情況下提供下列回應:
DXGI 1.1 變更
我們在 DXGI 1.1 中新增了下列功能。
同步共用表面支援
Direct3D 10.1 和 Direct3D 11 的同步處理共用介面可在多個 Direct3D 裝置之間有效率地讀取和寫入介面共用 (Direct3D 10 和 Direct3D 11 裝置之間可以共用) 。 請參閱 IDXGIKeyedMutex::AcquireSync 和 IDXGIKeyedMutex::ReleaseSync。
高色彩支持
支援DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM格式。
IDXGIDevice1::SetMaximumFrameLatency 和 IDXGIDevice1::GetMaximumFrameLatency
IDXGIFactory1::EnumAdapters1 會列舉沒有任何監視器或輸出附加的本機配接器,以及附加輸出的配接器 () 。 第一個傳回的配接卡將是桌面主顯示所在的本機配接卡。
BGRA 格式支援
DXGI_FORMAT_B8G8R8A8_UNORM和DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,請參閱 IDXGISurface1::GetDC 和 IDXGISurface1::ReleaseDC。
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 可能會套用應用程式行為修改,以增強應用程式相容性。
- 相容性設定可以儲存在使用者登錄
HKCU\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\AppCompatFlags\Layers中。 - 相容性設定可能會或可能不會套用,具體取決於系統配置或其他因素。
- 應用程式相容性設定值範例為
DXAllowModeChangeOnLaunch720。