將 EGL 程式碼與 DXGI 和 Direct3D 進行比較
重要 API
DirectX 圖形介面 (DXGI) 和多個 Direct3D API 的作用與 EGL 相同。 本主題可協助您從 EGL 的觀點瞭解 DXGI 和 Direct3D 11。
DXGI 和 Direct3D,例如 EGL,提供設定圖形資源的方法、取得著色器要繪製的轉譯內容,以及在視窗中顯示結果。 不過,DXGI 和 Direct3D 有更多的選項,而且在從 EGL 移植時需要更多努力才能正確設定。
注意:本指引是依據 Khronos Group 的 EGL 1.4 開放規格,可在此處找到:Khronos Native Platform Graphics Interface (EGL 版本 1.4 - April 6, 2011) [PDF]。 本指引並未涵蓋其他平台和開發語言特定語法的差異。
DXGI 和 Direct3D 相比如何?
與 DXGI 和 Direct3D 相比,EGL 的一大優點是開始繪製到視窗表面相對簡單。 這是因為 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) 應用功能 (例如螢幕旋轉) 提供了良好的基準。
相對於 Direct3D 11,EGL 的 API 非常少,如果您不熟悉該平台特有的命名和術語,那麼瀏覽後者可能會是一項挑戰。 以下是協助您取得導向的簡單概觀。
首先,檢閱 Direct3D 介面對應的基本 EGL 物件:
EGL 抽象化 | 類似的 Direct3D 表示法 |
---|---|
EGLDisplay | 在 Direct3D (對於 UWP 應用程式) 中,顯示控制代碼是透過 Windows::UI::CoreWindow API (或公開 HWND 的 ICoreWindowInterop) 取得的。 介面卡和硬體設定分別透過 IDXGIAdapter 和 IDXGIDevice1 介面進行設定。 |
EGLSurface | 在 Direct3D 中,緩衝區和其他視窗資源 (可見或螢幕外) 由特定 DXGI 介面建立和設定,包括 IDXGIFactory2 (用於取得 DXGI 資源的工廠模式實作,例如 IDXGISwapChain1 (顯示緩衝區)。 代表圖形裝置及其資源的 ID3D11Device1 透過 D3D11Device::CreateDevice 取得。 針對轉譯目標,請使用 ID3D11RenderTargetView 介面。 |
EGLContext | 在 Direct3D 中,您可以使用 ID3D11DeviceContext1 介面設定命令並向圖形管道發出命令。 |
EGLConfig | 在 Direct3D 11 中,您可以使用 ID3D11Device1 介面上的方法建立和設定圖形資源,例如緩衝區、紋理、樣板和著色器。 |
現在,以下是為 UWP 應用程式設定簡單圖形顯示、資源和內容的最基本程序。
- 透過呼叫 CoreWindow::GetForCurrentThread 來取得應用程式核心 UI 執行緒的 CoreWindow 物件的控制代碼。
- 對於 UWP 應用程,使用 IDXGIFactory2::CreateSwapChainForCoreWindow 從 IDXGIAdapter2 取得交換鏈,並將其傳遞給您在步驟 1 中獲得的 CoreWindow 參考。 您會在傳回中取得 IDXGISwapChain1 執行個體。 將其範圍設定為轉譯器物件及其轉譯執行緒。
- 透過呼叫 D3D11Device::CreateDevice 方法取得 ID3D11Device1 和 ID3D11DeviceContext1 執行個體。 將它們範圍限定於轉譯器物件。
- 使用轉譯器的 ID3D11Device1 物件上的方法建立著色器、紋理和其他資源。
- 使用轉譯器的 ID3D11DeviceContext1 物件上的方法定義緩衝區、執行著色器並管理管道階段。
- 當管線執行完畢並且一畫面被繪製到後端緩衝區時,使用 IDXGISwapChain1::Present1 將其呈現到螢幕上。
若要更詳細地檢查此程序,請檢閱 DirectX 圖形使用者入門。 本文的其餘部分涵蓋基本圖形管線設定和管理的許多常見步驟。
注意:Windows 桌面應用程式具有不同的 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 應用程式的主視窗由 CoreWindow 物件表示,可以透過呼叫 CoreWindow::GetForCurrentThread,做為為 Direct3D 建構的「檢視提供者」初始化程序的一部分,從應用程式物件取得該物件。 (如果使用 Direct3D-XAML Interop,則使用 XAML 架構的檢視提供者。) 如何設定應用程式以顯示檢視中介紹了建立 Direct3D 檢視提供者的程序。
取得 Direct3D 的 CoreWindow。
CoreWindow::GetForCurrentThread();
取得 CoreWindow 參考後,必須啟用視窗,這會執行主物件的 Run 方法並開始視窗事件處理。 之後,建立 ID3D11Device1 和 ID3D11DeviceContext1,並使用它們來取得基本 IDXGIDevice1 和 IDXGIAdapter,以便您可以獲得 IDXGIFactory2 物件,以依據 DXGI_SWAP_CHAIN_DESC1 設定建立交換鏈資源。
在適用於 Direct3D 的 CoreWindow 上設定 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 將顯示內容繫結至顯示器和介面。
在上一節中,我們建立了 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.
);
繪圖到紋理或 pixmap 資源
若要使用 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 多執行緒,請閱讀多執行緒。 |