重要的應用程式介面
DirectX 圖形介面 (DXGI) 和數個 Direct3D API 的作用與 EGL 相同。 本主題可協助您從 EGL 的觀點瞭解 DXGI 和 Direct3D 11。
DXGI 和 Direct3D 一樣,提供設定圖形資源的方法、取得讓著色器繪製的渲染上下文,並在視窗中顯示結果。 不過,DXGI 和 Direct3D 有更多的選項,而且在從 EGL 移植時需要更多努力才能正確設定。
附注 本指南是以 Khronos Group 的 EGL 1.4 開放規格為基礎,請參閱這裡:Khronos 原生平臺圖形介面 (EGL 1.4 - 2011 年 4 月 6 日) [PDF]。 本指南並未涵蓋其他平臺和開發語言特定語法的差異。
DXGI 和 Direct3D 有何比較?
EGL 優於 DXGI 和 Direct3D 的優點是,開始繪製到視窗表面相當簡單。 這是因為 OpenGL ES 2.0,因此 EGL 是多個平臺提供者所實作的規格,而 DXGI 和 Direct3D 是硬體廠商驅動程式必須符合的單一參考。 這表示Microsoft必須實作一組 API,以啟用最廣泛的廠商功能集合,而不是專注於特定廠商所提供的功能子集,或將廠商特定的安裝命令結合成更簡單的 API。 另一方面,Direct3D 提供一組單一 API,涵蓋非常廣泛的圖形硬體平臺和功能層級,併為平臺體驗的開發人員提供更多彈性。
如同 EGL,DXGI 和 Direct3D 也提供下列行為的 API:
- 取得、讀取與寫入框架緩衝區(在 DXGI 中稱為「交換鏈結」)。
- 將框架緩衝區與UI視窗產生關聯。
- 取得和設定用於繪製的渲染上下文。
- 針對特定轉譯內容向圖形管線發出命令。
- 建立和管理著色器資源,並將其與轉譯內容產生關聯。
- 渲染到特定的渲染目標(例如紋理)。
- 使用圖形資源渲染的結果來更新視窗的顯示表面。
若要查看設定圖形管線的基本 Direct3D 程式,請參閱 Microsoft Visual Studio 2015 中的 DirectX 11 應用程式(通用 Windows) 範本。 其中的基底轉譯類別提供良好的基準,可用來設定 Direct3D 11 圖形基礎結構,並在其上設定基本資源,以及支援通用 Windows 平臺 (UWP) 應用程式功能,例如螢幕旋轉。
EGL 與 Direct3D 11 相關的 API 很少,如果您不熟悉平臺特定的命名和行話,瀏覽後者可能是一項挑戰。 以下是協助您取得導向的簡單概觀。
首先,檢閱基本的 EGL 物件到 Direct3D 介面的對應:
| EGL 抽象層 | 類似的 Direct3D 表示法 |
|---|---|
| EGLDisplay | 在 Direct3D(適用於 UWP 應用程式)中,顯示句柄是透過 Windows::UI::CoreWindow API 或暴露 HWND 的 ICoreWindowInterop 介面取得的。 適配卡和硬體組態會分別使用 IDXGIAdapter 和 IDXGIDevice1 COM 介面來設定。 |
| EGLSurface | 在 Direct3D 中,緩衝區和其他視窗資源(可見或外畫面)是由特定的 DXGI 介面所建立和設定,包括 IDXGIFactory2(用來取得 DXGI 資源的處理站模式實作,例如IDXGISwapChain1(顯示緩衝區)。 代表圖形裝置及其資源的 ID3D11Device1 會使用 D3D11Device::CreateDevice取得。 針對轉譯目標,請使用 ID3D11RenderTargetView 介面。 |
| EGLContext | 在 Direct3D 中,您會使用 ID3D11DeviceContext1 介面,設定併發出命令給圖形管線。 |
| EGLConfig | 在 Direct3D 11 中,您可以使用 ID3D11Device1 介面上的方法,建立及設定圖形資源,例如緩衝區、紋理、樣板和著色器。 |
現在,這是用於UWP應用程式在DXGI和Direct3D中設定簡單圖形顯示、資源和上下文的最基本過程。
- 呼叫 CoreWindow::GetForCurrentThread,以取得應用程式核心 UI 線程 CoreWindow 物件的句柄。
- 針對UWP應用程式,先從IDXGIAdapter2 取得交換鏈結,然後使用IDXGIFactory2::CreateSwapChainForCoreWindow,將您在步驟1中取得的CoreWindow參考傳遞給它。 您會在傳回中取得 IDXGISwapChain1 實例。 將其範圍設定為轉譯器物件及其轉譯線程。
- 呼叫 D3D11Device::CreateDevice 方法來取得 ID3D11Device1 和 ID3D11DeviceContext1 實例。 將它們的作用域也設定於渲染器物件。
- 使用渲染器 ID3D11Device1 物件的方法,來建立著色器、紋理和其他資源。
- 使用您的渲染器的 ID3D11DeviceContext1 物件上的方法,來定義緩衝區、運行著色器並管理管線階段。
- 當管線執行且框架繪製到後台緩衝區時,將其呈現到具有 IDXGISwapChain1::Present1的螢幕。
若要更詳細地檢查此過程,請參閱 入門 DirectX 圖形。 本文的其餘部分涵蓋基本圖形管線設定和管理的許多常見步驟。
附注 Windows Desktop 應用程式有不同的 API 來取得 Direct3D 交換鏈結,例如 D3D11Device::CreateDeviceAndSwapChain,而且不使用 CoreWindow 物件。
取得顯示視窗
在此範例中,eglGetDisplay 會針對Microsoft Windows 平臺專屬的視窗資源傳遞 HWND。 其他平台,例如 Apple 的 iOS(Cocoa)和 Google 的 Android,對於視窗資源有不同的句柄或引用,並且可能具有完全不同的調用語法。 取得顯示器之後,您可以將其初始化、設定慣用的組態,並使用您可以繪製的後緩衝區來建立表面。
取得顯示器,並使用 EGL.進行設定。
// Obtain an EGL display object.
EGLDisplay display = eglGetDisplay(GetDC(hWnd));
if (display == EGL_NO_DISPLAY)
{
return EGL_FALSE;
}
// Initialize the display
if (!eglInitialize(display, &majorVersion, &minorVersion))
{
return EGL_FALSE;
}
// Obtain the display configs
if (!eglGetConfigs(display, NULL, 0, &numConfigs))
{
return EGL_FALSE;
}
// Choose the display config
if (!eglChooseConfig(display, attribList, &config, 1, &numConfigs))
{
return EGL_FALSE;
}
// Create a surface
surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)hWnd, NULL);
if (surface == EGL_NO_SURFACE)
{
return EGL_FALSE;
}
在 Direct3D 中,UWP app 的主視窗是由 CoreWindow 物件來表示,其可從 app 物件取得,方法是呼叫 CoreWindow::GetForCurrentThread,作為您為 Direct3D 建構之「檢視提供者」初始化程式的一部分。 (如果您使用 Direct3D-XAML Interop,請使用 XAML 架構的檢視提供者。)在 中有提到,如何設定應用程式以顯示檢視,以及建立 Direct3D 檢視提供者的過程。
取得 Direct3D 的 CoreWindow。
CoreWindow::GetForCurrentThread();
取得 CoreWindow 參考之後,就必須啟動視窗,以執行主物件的 Run 方法,並開始視窗事件處理。 之後,請建立 ID3D11Device1 和 ID3D11DeviceContext1,並使用它們來取得基礎 IDXGIDevice1 和 IDXGIAdapter,以便您可以取得 IDXGIFactory2 物件,以根據您的 DXGI_SWAP_CHAIN_DESC1 組態建立交換鏈資源。
在 CoreWindow 上配置和設定適用於 Direct3D 的 DXGI 交換鏈。
// Called when the CoreWindow object is created (or re-created).
void SimpleDirect3DApp::SetWindow(CoreWindow^ window)
{
// Register event handlers with the CoreWindow object.
// ...
// Obtain your ID3D11Device1 and ID3D11DeviceContext1 objects
// In this example, m_d3dDevice contains the scoped ID3D11Device1 object
// ...
ComPtr<IDXGIDevice1> dxgiDevice;
// Get the underlying DXGI device of the Direct3D device.
m_d3dDevice.As(&dxgiDevice);
ComPtr<IDXGIAdapter> dxgiAdapter;
dxgiDevice->GetAdapter(&dxgiAdapter);
ComPtr<IDXGIFactory2> dxgiFactory;
dxgiAdapter->GetParent(
__uuidof(IDXGIFactory2),
&dxgiFactory);
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Width = static_cast<UINT>(m_d3dRenderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = static_cast<UINT>(m_d3dRenderTargetSize.Height);
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All UWP apps must use this SwapEffect.
swapChainDesc.Flags = 0;
// ...
Windows::UI::Core::CoreWindow^ window = m_window.Get();
dxgiFactory->CreateSwapChainForCoreWindow(
m_d3dDevice.Get(),
reinterpret_cast<IUnknown*>(window),
&swapChainDesc,
nullptr, // Allow on all displays.
&m_swapChainCoreWindow);
}
在您準備好畫面以顯示後,呼叫 IDXGISwapChain1::Present1 方法。
請注意,在 Direct3D 11 中,沒有與 EGLSurface 相同的抽象概念。 (有 IDXGISurface1,但它的使用方式有所不同。)最接近的概念是 ID3D11RenderTargetView 物件,我們用來將紋理(ID3D11Texture2D)指派為著色器管線繪製後的緩衝區。
在 Direct3D 11 中設定交換鏈結的備份緩衝區
ComPtr<ID3D11RenderTargetView> m_d3dRenderTargetViewWin; // scoped to renderer object
// ...
ComPtr<ID3D11Texture2D> backBuffer2;
m_swapChainCoreWindow->GetBuffer(0, IID_PPV_ARGS(&backBuffer2));
m_d3dDevice->CreateRenderTargetView(
backBuffer2.Get(),
nullptr,
&m_d3dRenderTargetViewWin);
最佳做法是在建立視窗或變更大小時呼叫此程序代碼。 在渲染期間,使用 ID3D11DeviceContext1::OMSetRenderTargets 設定渲染目標檢視,然後再設置其他子資源,例如頂點緩衝區或著色器。
// Set the render target for the draw operation.
m_d3dContext->OMSetRenderTargets(
1,
d3dRenderTargetView.GetAddressOf(),
nullptr);
建立渲染上下文
在 EGL 1.4 中,「顯示」代表一組窗口資源。 通常,您可以透過提供一組屬性給顯示物件來設定顯示的「表面」,並獲得一個表面作為回報。 您可以建立內容來顯示介面的內容,方法是建立該內容並將它系結至介面和顯示器。
呼叫流程通常如下所示:
- 呼叫 eglGetDisplay 並傳入顯示或視窗資源的句柄,取得顯示物件。
- 使用 eglInitialize 初始化顯示。
- 取得可用的顯示組態,並使用 eglGetConfigs 和 eglChooseConfig 選取一個。
- 使用 eglCreateWindowSurface 建立視窗表面。
- 使用 eglCreateContext 建立繪圖上下文。
- 使用 eglMakeCurrent 將顯示內容系結至顯示器和介面。
n 上一節中,我們建立了 EGLDisplay 和 EGLSurface,現在我們使用 EGLDisplay 來建立內容,並將該內容與顯示產生關聯,並使用設定的 EGLSurface 來參數化輸出。
使用 EGL 1.4 獲取渲染上下文
// Configure your EGLDisplay and obtain an EGLSurface here ...
// ...
// Create a drawing context from the EGLDisplay
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT)
{
return EGL_FALSE;
}
// Make the context current
if (!eglMakeCurrent(display, surface, surface, context))
{
return EGL_FALSE;
}
Direct3D 11 中的轉譯內容是由 ID3D11Device1 物件來表示,該物件代表配接器,並可讓您建立 Direct3D 資源,例如緩衝區和著色器;和 由 ID3D11DeviceContext1 物件,可讓您管理圖形管線並執行著色器。
請注意 Direct3D 功能層級! 這些是用來支援從 DirectX 9.1 到 DirectX 11 的舊版 Direct3D 硬體平臺。 許多使用低功率圖形硬體的平臺,例如平板電腦,只能存取 DirectX 9.1 功能,而較舊的支持圖形硬體可以從 9.1 到 11。
使用 DXGI 和 Direct3D 建立渲染上下文
// ...
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
ComPtr<IDXGIDevice> dxgiDevice;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> d3dContext;
D3D11CreateDevice(
nullptr, // Specify nullptr to use the default adapter.
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
creationFlags, // Set debug and Direct2D compatibility flags.
featureLevels, // List of feature levels this app can support.
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for UWP apps.
&device, // Returns the Direct3D device created.
&m_featureLevel, // Returns feature level of device created.
&d3dContext // Returns the device immediate context.
);
繪圖到材質或像素圖資源
若要使用 OpenGL ES 2.0 繪製到紋理,請設定圖元緩衝區或 PBuffer。 在成功配置並為其創建 EGLSurface 之後,您可以為其提供渲染上下文,並執行著色器管線來繪製到紋理中。
使用 OpenGL ES 2.0 繪製到像素緩衝區
// Create a pixel buffer surface to draw into
EGLConfig pBufConfig;
EGLint totalpBufAttrs;
const EGLint pBufConfigAttrs[] =
{
// Configure the pBuffer here...
};
eglChooseConfig(eglDsplay, pBufConfigAttrs, &pBufConfig, 1, &totalpBufAttrs);
EGLSurface pBuffer = eglCreatePbufferSurface(eglDisplay, pBufConfig, EGL_TEXTURE_RGBA);
在 Direct3D 11 中,您會建立 ID3D11Texture2D 資源,並將其設為轉譯目標。 使用 D3D11_RENDER_TARGET_VIEW_DESC配置繪製目標。 當您使用此渲染目標呼叫 ID3D11DeviceContext::Draw 方法(或在設備上下文上進行類似的 Draw* 操作)時,結果會被繪製到紋理中。
使用 Direct3D 11 繪製到紋理
ComPtr<ID3D11Texture2D> renderTarget1;
D3D11_RENDER_TARGET_VIEW_DESC renderTargetDesc = {0};
// Configure renderTargetDesc here ...
m_d3dDevice->CreateRenderTargetView(
renderTarget1.Get(),
nullptr,
&m_d3dRenderTargetViewWin);
// Later, in your render loop...
// Set the render target for the draw operation.
m_d3dContext->OMSetRenderTargets(
1,
d3dRenderTargetView.GetAddressOf(),
nullptr);
如果此紋理與 ID3D11ShaderResourceView相關聯,則可以將其傳遞至著色器。
繪製到螢幕
一旦您使用 EGLContext 來設定緩衝區並更新您的數據,您就會執行系結至該緩衝區的著色器,並使用 glDrawElements 將結果繪製到後台緩衝區。 您可以呼叫 eglSwapBuffers 來顯示後台緩衝區。
開啟 GL ES 2.0:繪製到畫面。
glDrawElements(GL_TRIANGLES, renderer->numIndices, GL_UNSIGNED_INT, 0);
eglSwapBuffers(drawContext->eglDisplay, drawContext->eglSurface);
在 Direct3D 11 中,您可以設定緩衝區,並將著色器綁定到 IDXGISwapChain::Present1。 然後,您呼叫其中一個 ID3D11DeviceContext1::Draw* 方法來運行著色器,並將結果繪製到配置為交換鏈的後緩衝區的渲染目標。 之後,您只要呼叫 IDXGISwapChain::Present1,即可將後台緩衝區呈現到顯示器。
Direct3D 11:繪製到螢幕。
m_d3dContext->DrawIndexed(
m_indexCount,
0,
0);
// ...
m_swapChainCoreWindow->Present1(1, 0, ¶meters);
釋放圖形資源
在 EGL 中,您會將 EGLDisplay 傳遞至 eglTerminate 來釋放窗口資源。
使用 EGL 1.4 終止顯示
EGLBoolean eglTerminate(eglDisplay);
在 UWP 應用程式中,您可以使用 CoreWindow::Close關閉 CoreWindow,不過這隻能用於次要 UI 視窗。 無法關閉主要 UI 執行緒及其相關聯的 CoreWindow,相反地,這些是由操作系統來終止。 不過,當次要 CoreWindow 關閉時,會引發 CoreWindow::Closed 事件。
EGL 與 Direct3D 11 的 API 參考對應
| EGL API | 類似的 Direct3D 11 API 或行為 |
|---|---|
| eglBindAPI | N/A。 |
| eglBindTexImage | 呼叫 ID3D11Device::CreateTexture2D 來設定 2D 紋理。 |
| eglChooseConfig | Direct3D 不提供一組預設框架緩衝區組態。 交換鏈結的組態 |
| eglCopyBuffers | 若要複製緩衝區數據,請呼叫 ID3D11DeviceContext::CopyStructureCount。 若要複製資源,請呼叫 ID3DDeviceCOntext::CopyResource。 |
| eglCreateContext | 呼叫 D3D11CreateDevice來建立 Direct3D 裝置內容,這會同時傳回 Direct3D 裝置的控制代碼和預設 Direct3D 立即上下文(ID3D11DeviceContext1 物件)。 您也可以在傳回的 ID3D11Device1 物件上呼叫 ID3D11Device2::CreateDeferredContext,以建立 Direct3D 延遲上下文。 |
| eglCreatePbufferFromClientBuffer | 所有緩衝區都會讀取和寫入為 Direct3D 子資源,例如 ID3D11Texture2D。 使用 等方法,例如 ID3D11DeviceContext1:CopyResource,將資源從一個相容的子資源類型複製到另一個。 |
| eglCreatePbufferSurface | 若要建立沒有交換鏈結的 Direct3D 裝置,請呼叫靜態 D3D11CreateDevice 方法。 針對 Direct3D 呈現目標檢視,呼叫 ID3D11Device::CreateRenderTargetView。 |
| eglCreatePixmapSurface | 若要建立沒有交換鏈結的 Direct3D 裝置,請呼叫靜態 D3D11CreateDevice 方法。 針對 Direct3D 呈現目標檢視,呼叫 ID3D11Device::CreateRenderTargetView。 |
| eglCreateWindowSurface | 取得 IDXGISwapChain1(適用於顯示緩衝區)和 ID3D11Device1(圖形裝置及其資源的虛擬介面)。 使用 ID3D11Device1 來定義 ID3D11RenderTargetView,可用來建立您提供給 IDXGISwapChain1的框架緩衝區。 |
| eglDestroyContext | N/A。 使用 ID3D11DeviceContext::DiscardView1 來清除轉譯目標檢視。 若要關閉父 ID3D11DeviceContext1,請將 實例設定為 null,並等候平臺回收其資源。 您無法直接摧毀設備上下文。 |
| eglDestroySurface | N/A。 當 UWP 應用程式的 CoreWindow 由平台關閉時,會清除圖形資源。 |
| eglGetCurrentDisplay | 呼叫 CoreWindow:GetForCurrentThread,以獲取當前主應用程式窗口的引用。 |
| eglGetCurrentSurface | 這是目前 ID3D11RenderTargetView。 一般而言,這會限定於您的渲染器對象。 |
| eglGetError | 錯誤會以 DirectX 介面上大部分方法所傳回的 HRESULT 來表示。 如果方法未傳回 HRESULT,請呼叫 GetLastError。 若要將系統錯誤轉換為 HRESULT 值,請使用 HRESULT_FROM_WIN32 巨集。 |
| eglInitialize | 呼叫 CoreWindow:GetForCurrentThread,以獲取當前主應用程式窗口的引用。 |
| eglMakeCurrent | 使用 ID3D11DeviceContext1::OMSetRenderTargets設置在當前上下文上繪製的渲染目標。 |
| eglQueryContext | N/A。 不過,您可以從 ID3D11Device1 實例中取得渲染目標和一些配置數據。 (請參閱可用方法列表的連結。 |
| eglQuerySurface | N/A。 不過,您可以從 ID3D11Device1 實例的方法中,取得有關視口及目前圖形硬體的數據。 (請參閱可用方法列表的連結。 |
| eglReleaseTexImage | N/A。 |
| eglReleaseThread | 如需一般 GPU 多線程,請閱讀 多線程。 |
| eglSurfaceAttrib | 使用 D3D11_RENDER_TARGET_VIEW_DESC 來配置 Direct3D 渲染目標視圖, |
| eglSwapBuffers | 使用 IDXGISwapChain1::Present1。 |
| eglSwapInterval | 請參閱 IDXGISwapChain1。 |
| eglTerminate | 用來顯示圖形管線輸出的 CoreWindow 是由作業系統所管理。 |
| eglWaitClient | 針對共用表面,請使用IDXGIKeyedMutex。 如需一般 GPU 多線程,請閱讀 多線程。 |
| eglWaitGL | 針對共用表面,請使用IDXGIKeyedMutex。 如需一般 GPU 多線程,請閱讀 多線程。 |
| eglWaitNative | 針對共用表面,請使用IDXGIKeyedMutex。 如需一般 GPU 多線程,請閱讀 多線程。 |