共用方式為


比較EGL程式代碼與 DXGI 和 Direct3D

重要的應用程式介面

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 介面取得的。 適配卡和硬體組態會分別使用 IDXGIAdapterIDXGIDevice1 COM 介面來設定。
EGLSurface 在 Direct3D 中,緩衝區和其他視窗資源(可見或外畫面)是由特定的 DXGI 介面所建立和設定,包括 IDXGIFactory2(用來取得 DXGI 資源的處理站模式實作,例如IDXGISwapChain1(顯示緩衝區)。 代表圖形裝置及其資源的 ID3D11Device1 會使用 D3D11Device::CreateDevice取得。 針對轉譯目標,請使用 ID3D11RenderTargetView 介面。
EGLContext 在 Direct3D 中,您會使用 ID3D11DeviceContext1 介面,設定併發出命令給圖形管線。
EGLConfig 在 Direct3D 11 中,您可以使用 ID3D11Device1 介面上的方法,建立及設定圖形資源,例如緩衝區、紋理、樣板和著色器。

 

現在,這是用於UWP應用程式在DXGI和Direct3D中設定簡單圖形顯示、資源和上下文的最基本過程。

  1. 呼叫 CoreWindow::GetForCurrentThread,以取得應用程式核心 UI 線程 CoreWindow 物件的句柄。
  2. 針對UWP應用程式,先從IDXGIAdapter2 取得交換鏈結,然後使用IDXGIFactory2::CreateSwapChainForCoreWindow,將您在步驟1中取得的CoreWindow參考傳遞給它。 您會在傳回中取得 IDXGISwapChain1 實例。 將其範圍設定為轉譯器物件及其轉譯線程。
  3. 呼叫 D3D11Device::CreateDevice 方法來取得 ID3D11Device1ID3D11DeviceContext1 實例。 將它們的作用域也設定於渲染器物件。
  4. 使用渲染器 ID3D11Device1 物件的方法,來建立著色器、紋理和其他資源。
  5. 使用您的渲染器的 ID3D11DeviceContext1 物件上的方法,來定義緩衝區、運行著色器並管理管線階段。
  6. 當管線執行且框架繪製到後台緩衝區時,將其呈現到具有 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 方法,並開始視窗事件處理。 之後,請建立 ID3D11Device1ID3D11DeviceContext1,並使用它們來取得基礎 IDXGIDevice1IDXGIAdapter,以便您可以取得 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, &parameters);

釋放圖形資源

在 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 多線程,請閱讀 多線程