OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow

时间:2024-11-30 09:05:08

1. EGL概述

EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它主要由系统制造商实现。

OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow

EGL提供如下机制:

  • 与设备的原生窗口系统通信
  • 查询绘图表面的可用类型和配置
  • 创建绘图表面
  • 在OpenGL ES 和其他图形渲染API之间同步渲染
  • 管理纹理贴图等渲染资源
  • 为了让OpenGL ES能够绘制在当前设备上,我们需要EGL作为OpenGL ES与设备的桥梁。

OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow

2. 使用EGL绘图的基本步骤

OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow

Display(EGLDisplay) 是对实际显示设备的抽象。

Surface(EGLSurface)是对用来存储图像的内存区域。

FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer。Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息。

使用EGL的绘图的一般步骤:

、获取 EGL Display 对象:eglGetDisplay()
、初始化与 EGLDisplay 之间的连接:eglInitialize()
、获取 EGLConfig 对象:eglChooseConfig()
、创建 EGLContext 实例:eglCreateContext()
、创建 EGLSurface 实例:eglCreateWindowSurface()
、连接 EGLContext 和 EGLSurface:eglMakeCurrent()
、使用 OpenGL ES API 绘制图形:gl_*()
、切换 front buffer 和 back buffer 送显:eglSwapBuffer()
、断开并释放与 EGLSurface 关联的 EGLContext 对象:eglRelease()
、删除 EGLSurface 对象
、删除 EGLContext 对象
、终止与 EGLDisplay 之间的连接

标准 EGL 数据类型如下所示:

EGLBoolean —— EGL_TRUE =1, EGL_FALSE=0
EGLint —— int 数据类型
EGLDisplay —— 系统显示 ID 或句柄,可以理解为一个前端的显示窗口
EGLConfig —— Surface的EGL配置,可以理解为绘制目标framebuffer的配置属性
EGLSurface —— 系统窗口或 frame buffer 句柄 ,可以理解为一个后端的渲染目标窗口。
EGLContext —— OpenGL ES 图形上下文,它代表了OpenGL状态机;如果没有它,OpenGL指令就没有执行的环境。

下面几个类型比较复杂,通过例子可以更深入的理解。这里要说明的是这几个类型在不同平台其实现是不同的,EGL只提供抽象标准。

NativeDisplayType—— Native 系统显示类型,标识你所开发设备的物理屏幕

NativeWindowType —— Native 系统窗口缓存类型,标识系统窗口

NativePixmapType —— Native 系统 frame buffer,可以作为 Framebuffer 的系统图像(内存)数据类型,该类型只用于离屏渲染。

3. EGLSurface

OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow

EGLSurface 可以是由 EGL 分配的离屏缓冲区(称为“pbuffer”),或由操作系统分配的窗口。EGL 窗口 Surface 通过 eglCreateWindowSurface() 调用被创建。该调用将“窗口对象”作为参数,在 Android 上,该对象可以是 SurfaceView、SurfaceTexture、SurfaceHolder 或 Surface,所有这些对象下面都有一个 BufferQueue。当您进行此调用时,EGL 将创建一个新的 EGLSurface 对象,并将其连接到窗口对象的 BufferQueue 的生产方接口。此后,渲染到该 EGLSurface 会导致一个缓冲区离开队列、进行渲染,然后排队等待消耗方使用。(术语“窗口”表示预期用途,但请注意,输出内容不一定会显示在显示屏上。)

EGL 不提供锁定/解锁调用,而是由您发出绘制命令,然后调用 eglSwapBuffers() 来提交当前帧。方法名称来自传统的前后缓冲区交换,但实际实现可能会有很大的不同。

一个 Surface 一次只能与一个 EGLSurface 关联(您只能将一个生产方连接到一个 BufferQueue),但是如果您销毁该 EGLSurface,它将与该 BufferQueue 断开连接,并允许其他内容连接到该 BufferQueue。

通过更改“当前”EGLSurface,指定线程可在多个 EGLSurface 之间进行切换。一个 EGLSurface 一次只能在一个线程上处于当前状态。

关于 EGLSurface 最常见的一个错误理解就是假设它只是 Surface 的另一方面(如 SurfaceHolder)。它是一个相关但独立的概念。您可以在没有 Surface 作为支持的 EGLSurface 上绘制,也可以在没有 EGL 的情况下使用 Surface。EGLSurface 仅为 GLES 提供一个绘制的地方。

4. ANativeWindow

公开的 Surface 类以 Java 编程语言实现。C/C++ 中的同等项是 ANativeWindow 类,由 Android NDK 半公开。您可以使用 ANativeWindow_fromSurface() 调用从 Surface 获取 ANativeWindow。就像它的 Java 语言同等项一样,您可以对 ANativeWindow 进行锁定、在软件中进行渲染,以及解锁并发布。

要从原生代码创建 EGL 窗口 Surface,可将 EGLNativeWindowType 的实例传递到 eglCreateWindowSurface()。EGLNativeWindowType 是 ANativeWindow 的同义词,您可以*地在它们之间转换。

基本的“原生窗口”类型只是封装 BufferQueue 的生产方,这一点并不足为奇。

5. EGLSurface与ANativeWindow之间的关系

OpenGL ES 定义了一个渲染图形的 API,但没有定义窗口系统。为了让 GLES 能够适合各种平台,GLES 将与知道如何通过操作系统创建和访问窗口的库结合使用。用于 Android 的库称为 EGL。如果要绘制纹理多边形,应使用 GLES 调用;如果要在屏幕上进行渲染,应使用 EGL 调用。

在使用 GLES 进行任何操作之前,需要创建一个 GL 上下文。在 EGL 中,这意味着要创建一个 EGLContext 和一个 EGLSurface。GLES 操作适用于当前上下文,该上下文通过线程局部存储访问,而不是作为参数进行传递。这意味着您必须注意渲染代码在哪个线程上执行,以及该线程上的当前上下文。

参考链接:

1. Android Display System(2)

2. OpenGL ES 2.0 知识串讲