Android EGL小结

1.概述

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

EGL 主要功能是作为 OpenGL ES 与设备的桥梁,以实现让 OpenGL ES 能够在当前设备上进行绘制。

 

EGL 提供如下机制:

  • 与设备的原生窗口系统通信;
  • 查询绘图图层的可用类型和配置;
  • 创建绘图图层;
  • 在 OpenGL ES 和其他图形渲染 API 之间同步渲染;
  • 管理纹理贴图等渲染资源。

 

EGL支持三种类型的surface渲染,分别是window、pixmap和pixel buffer。其中,window和pixmap类型的surface与本地窗口系统对应的资源息息相关;而pixel buffer则是EGL资源独享的,不可通过本地窗口系统进行渲染。
 

2.Android EGL Overview

 2.1使用EGL绘图的基本步骤

Display是对实际显示设备的抽象,Android上的实现类是EGLDisplay。

Surface是对用来存储图像的内存区域,Android上的实现类是EGLSurface。

FrameBuffer的抽象,包括color buffer,stencil buffer,depth buffer

Context 存储 OpenGL ES 绘图的状态信息,即OpenGL中的重要概念context(上下文)。在 Android 上的实现类是 EGLContext

 

 

2.2 EGL绘图步骤解析

 

使用 EGL 在平台实现渲染步骤大致如下:

1)调用 eglGetDisplay 来获得 EGLDisplay 对象,从而建立与平台窗口系统的联系,这个 EGLDisplay 将作为 OpenGL ES 的渲染目标;


2)调用 eglInitialize初始化 EGL;


3)调用 eglChooseConfig 来获得 EGLConfig 对象,从而确定渲染表面的配置信息:有两种方法,一种是查询每个Surface配置,找出最好的选择,另一种是指定一组需求,让EGL推荐最佳匹配,两者都返回一个EGLConfig,包括Surface相关的所有属性。在许多情况下,使用第二种方法更简单


4)通过 EGLDisplay 和 EGLConfig 对象,调用 eglCreateWindowSurface 或 eglCreatePbufferSurface 方法得到 EGLSurface,从而创建渲染表面,其中 eglCreateWindowSurface 用于创建屏幕上渲染区域,eglCreatePbufferSurface 用于创建离屏渲染区域;和窗口一样,PBuffer可以利用OpenGL ES 3.0中的任何硬件加速,PBuffer最常用于生成纹理贴图,如果想要做的是渲染到一个纹理,那么建议使用帧缓冲区对象(FBO)代替PBuffer,因为帧缓冲区更高效,不过在某些FBO无法使用的情况下,PBuffer仍然有用,例如用OpenGL ES在屏幕外表面上渲染,然后将其作为其它API(如OpenVG)中的纹理。另外,EGLSurface还有个Pixmap Surface,简单总结一下三者的特点。window是on-screen的,pbuffer和pixmap是off-screen的,window绑定到了NativeWindow,pixmap绑定到了NativePixmap,pbuffer没有任何本地绑定,window是双缓冲区的,pbuffer和pixmap是单缓冲区的,window默认在back buffer渲染,通过eglSwapBuffers交换到屏幕上显示,pbuffer在显存中分配,由EGL_HEIGHT和EGL_WIDTH指定大小,常用作纹理数据,pixmap绑定到本地的像素缓冲区,这个缓冲区可以被其它API使用。创建不同的EGLSurface时,需要在EGLConfig中配置EGL_SURFACE_TYPE,window、pbuffer、pixmap分别对应于EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BUFFER。


5)通过 EGLDisplay 和 EGLConfig,调用 eglCreateContext 获得 EGLContext 对象,从而创建渲染上下文,OpenGL 的任何一条指令都是必须在自己的 OpenGL 上下文环境中执行;


6)调用 eglMakeCurrent 将 EGLSurface、EGLContext、EGLDisplay 三者绑定就完成了上下文绑定,绑定成功之后 OpenGL ES 的环境就创建好了,接下来就可以开始渲染了;
通过上面的步骤就做好了 EGL 的准备工作:一方面为 OpenGL ES 渲染提供了目标 EGLDisplay 及上下文环境 EGLContext,可以接收到 OpenGl ES 渲染出来的纹理;另一方面我们连接好了设备显示屏 EGLSurface(这里可能是 SurfaceView 或者 TextureView)。接下来,由于 OpenGL ES 的渲染必须新开一个线程,并为该线程绑定显示设备及上下文环境(EGLContext),所以 eglMakeCurrent() 就是来绑定该线程的显示设备及上下文的。


7)OpenGL ES 完成绘制后,调用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上,而离屏渲染不需要调用此方法;
这里需要注意的是 EGL 的工作模式是双缓冲模式,其内部有两个 FrameBuffer(帧缓冲区):BackFrameBuffer 和 FrontFrameBuffer,当 EGL 将一个 FrameBuffer 显示到屏幕上的时候,另一个 FrameBuffer 就在后台等待 OpenGL ES 进行渲染输出。
直到调用了 eglSwapBuffer() 这条指令的时候,才会把前台的 FrameBuffer 和后台的 FrameBuffer 进行交换,这时界面呈现的就是 OpenGL ES 刚刚渲染的内容了。
这样做的原因是如果应用程序使用单缓冲绘图时可能会存在图像闪烁的问题,因为图像生成不是一下子被绘制出来的,而是按照从左到右、从上到下逐像素绘制的。如果最终图像不是在瞬间全部展示给用户,而是通过把绘制过程也展示出来了,这会导致用户看到的渲染效果出现闪烁。为了规避这个问题,可以使用双缓冲渲染:前缓冲保存着最终输出的图像,它会在屏幕上显示;而所有的的渲染指令都会在后缓冲上绘制,对用户屏蔽从左到右、从上到下逐像素绘制的过程,这样就可以避免闪烁了。


8)绘制结束后,不再需要使用 EGL 时,需要调用 eglMakeCurrent 取消绑定,调用 eglDestroyContext、eglDestroySurface、eglTerminate 等函数销毁 EGLDisplay、EGLSurface、EGLContext 三个对象。
在《RenderDemo(1):用 OpenGL 画一个三角形》 Android Demo 的 KFGLContext 类中就可以看到上面这套流程。

不过,如果觉得上述配置 EGL 的流程太麻烦的话,Android 平台提供了 GLSurfaceView类实现了 Display、Surface、Context 的管理,即 GLSurfaceView 内部实现了对 EGL 的封装,可以很方便地利用接口 GLSurfaceView.Renderer 的实现,使用 OpenGL ES API 进行渲染绘制。GLSurfaceView 提升了 OpenGL ES 开发的便利性,当然也相应的失去了一些灵活性。

 



参考链接:
  • 一看就懂的 OpenGL 基础概念(2):EGL,OpenGL 与设备的桥梁丨音视频基础: https://zhuanlan.zhihu.com/p/579253989 
  • EGL介绍 : https://www.jianshu.com/p/bc84a293e254
  • OpenGL ES: (3) EGL、EGL绘图的基本步骤、EGLSurface、ANativeWindow: https://blog.csdn.net/xiaoyafang123/article/details/125458638
 


 

posted @ 2024-02-18 15:25  青山牧云人  阅读(623)  评论(0编辑  收藏  举报