共用方式為


將 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) 取得的。 介面卡和硬體設定分別透過 IDXGIAdapterIDXGIDevice1 介面進行設定。
EGLSurface 在 Direct3D 中,緩衝區和其他視窗資源 (可見或螢幕外) 由特定 DXGI 介面建立和設定,包括 IDXGIFactory2 (用於取得 DXGI 資源的工廠模式實作,例如 IDXGISwapChain1 (顯示緩衝區)。 代表圖形裝置及其資源的 ID3D11Device1 透過 D3D11Device::CreateDevice 取得。 針對轉譯目標,請使用 ID3D11RenderTargetView 介面。
EGLContext 在 Direct3D 中,您可以使用 ID3D11DeviceContext1 介面設定命令並向圖形管道發出命令。
EGLConfig 在 Direct3D 11 中,您可以使用 ID3D11Device1 介面上的方法建立和設定圖形資源,例如緩衝區、紋理、樣板和著色器。

 

現在,以下是為 UWP 應用程式設定簡單圖形顯示、資源和內容的最基本程序。

  1. 透過呼叫 CoreWindow::GetForCurrentThread 來取得應用程式核心 UI 執行緒的 CoreWindow 物件的控制代碼。
  2. 對於 UWP 應用程,使用 IDXGIFactory2::CreateSwapChainForCoreWindowIDXGIAdapter2 取得交換鏈,並將其傳遞給您在步驟 1 中獲得的 CoreWindow 參考。 您會在傳回中取得 IDXGISwapChain1 執行個體。 將其範圍設定為轉譯器物件及其轉譯執行緒。
  3. 透過呼叫 D3D11Device::CreateDevice 方法取得 ID3D11Device1ID3D11DeviceContext1 執行個體。 將它們範圍限定於轉譯器物件。
  4. 使用轉譯器的 ID3D11Device1 物件上的方法建立著色器、紋理和其他資源。
  5. 使用轉譯器的 ID3D11DeviceContext1 物件上的方法定義緩衝區、執行著色器並管理管道階段。
  6. 當管線執行完畢並且一畫面被繪製到後端緩衝區時,使用 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 方法並開始視窗事件處理。 之後,建立 ID3D11Device1ID3D11DeviceContext1,並使用它們來取得基本 IDXGIDevice1IDXGIAdapter,以便您可以獲得 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, &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 多執行緒,請閱讀多執行緒