将 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 本机平台图形接口(EGL 版本 1.4 - 2011 年 4 月 6 日)[PDF]。 本指南未介绍特定于其他平台和开发语言的语法差异。

 

DXGI 和 Direct3D 的比较方式是什么?

EGL 相对于 DXGI 和 Direct3D 的巨大优势是,开始绘制到窗口图面相对简单。 这是因为 OpenGL ES 2.0(因此 EGL)是由多个平台提供商实现的规范,而 DXGI 和 Direct3D 是硬件供应商驱动程序必须遵循的单个引用。 这意味着,Microsoft必须实现一组 API,这些 API 可实现尽可能广泛的供应商功能集,而不是专注于特定供应商提供的功能子集,或者将特定于供应商的设置命令组合成更简单的 API。 另一方面,Direct3D 提供了一组 API,这些 API 涵盖非常广泛的图形硬件平台和功能级别,并为使用该平台的开发人员提供更多的灵活性。

与 EGL 一样,DXGI 和 Direct3D 为以下行为提供 API:

  • 在 DXGI 中获取帧缓冲区(称为“交换链”)并对其进行读取和写入。
  • 将帧缓冲区与 UI 窗口相关联。
  • 获取并配置用于绘图的渲染上下文。
  • 在特定呈现上下文中向图形管道发出命令。
  • 创建和管理着色器资源,并将其与呈现内容相关联。
  • 渲染到特定渲染目标(如纹理)。
  • 使用图形资源呈现结果来更新窗口的显示表面。

若要查看用于配置图形管道的基本 Direct3D 过程,请查看 Visual Studio 2015 Microsoft 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 接口上的方法创建和配置图形资源,例如缓冲区、纹理、模具和着色器。

 

现在,下面是在 DXGI 和 Direct3D 中为 UWP 应用设置简单图形显示、资源和上下文的最基本过程。

  1. 通过调用 CoreWindow::GetForCurrentThread,获取应用核心 UI 线程的 CoreWindow 对象的句柄。
  2. 对于 UWP 应用,请使用 IDXGIFactory2::CreateSwapChainForCoreWindowIDXGIAdapter2 获取交换链,并将其传递给在步骤 1 中获取的 CoreWindow 引用。 你将获得 IDXGISwapChain1 实例。 将其限定为呈现器对象及其呈现线程。
  3. 通过调用 D3D11Device::CreateDevice 方法获取 ID3D11Device1 和 ID3D11DeviceContext1 实例。 将它们的作用域限定为您的呈现器对象。
  4. 使用呈现器的 ID3D11Device1 对象上的方法创建着色器、纹理和其他资源。
  5. 使用呈现器的 ID3D11DeviceContext1 对象上的方法定义缓冲区、运行着色器和管理管道阶段。
  6. 当管道执行并且帧绘制到后台缓冲区时,将其呈现到屏幕上 IDXGISwapChain1::Present1

若要更详细地检查此过程,请查看 DirectX 图形入门。 本文的其余部分介绍了基本图形管道设置和管理的许多常见步骤。

注意 Windows 桌面应用具有用于获取 Direct3D 交换链的不同 API,例如 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 互作,请使用 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,但它的使用方式不同。最接近的概念是我们用于将纹理(ID3D11Texture2D)分配为着色器管道将要绘制到的后缓冲区的 ID3D11RenderTargetView 对象。)

在 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 中,“display”表示一组窗口资源。 通常,通过向显示对象提供一组属性并获取返回图面来为显示器配置“surface”。 通过创建该上下文并将其绑定到图面和显示器,可以创建用于显示图面内容的上下文。

调用流通常如下所示:

  • 使用显示或窗口资源的句柄调用 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 功能级别! 这些平台用于支持较旧的 Direct3D 硬件平台,从 DirectX 9.1 到 DirectX 11。 许多使用低功率图形硬件(如平板电脑)的平台只能访问 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 来显示后台缓冲区。

OpenGL 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 Obtain 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 多线程处理,请阅读 多线程处理