重要的应用程序接口(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 接口)获取的。 适配器和硬件配置分别使用 IDXGIAdapter 和 IDXGIDevice1 COM 接口进行设置。 |
EGLSurface | 在 Direct3D 中,缓冲区和其他窗口资源(可见或屏幕外)由特定的 DXGI 接口创建和配置,包括 IDXGIFactory2 (用于获取 DXGI 资源(例如IDXGISwapChain1 (显示缓冲区)的工厂模式实现)。 ID3D11Device1,表示图形设备和其资源,通过 D3D11Device::CreateDevice获取。 对于呈现目标,请使用 ID3D11RenderTargetView 接口。 |
EGLContext | 在 Direct3D 中,使用 ID3D11DeviceContext1 接口配置和向图形管道发出命令。 |
EGLConfig | 在 Direct3D 11 中,可以使用 ID3D11Device1 接口上的方法创建和配置图形资源,例如缓冲区、纹理、模具和着色器。 |
现在,下面是在 DXGI 和 Direct3D 中为 UWP 应用设置简单图形显示、资源和上下文的最基本过程。
- 通过调用 CoreWindow::GetForCurrentThread,获取应用核心 UI 线程的 CoreWindow 对象的句柄。
- 对于 UWP 应用,请使用 IDXGIFactory2::CreateSwapChainForCoreWindow从 IDXGIAdapter2 获取交换链,并将其传递给在步骤 1 中获取的 CoreWindow 引用。 你将获得 IDXGISwapChain1 实例。 将其限定为呈现器对象及其呈现线程。
- 通过调用 D3D11Device::CreateDevice 方法获取 ID3D11Device1 和 ID3D11DeviceContext1 实例。 将它们的作用域限定为您的呈现器对象。
- 使用呈现器的 ID3D11Device1 对象上的方法创建着色器、纹理和其他资源。
- 使用呈现器的 ID3D11DeviceContext1 对象上的方法定义缓冲区、运行着色器和管理管道阶段。
- 当管道执行并且帧绘制到后台缓冲区时,将其呈现到屏幕上 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 方法并开始窗口事件处理。 之后,创建 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,但它的使用方式不同。最接近的概念是我们用于将纹理(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, ¶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 | 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 多线程处理,请阅读 多线程处理。 |