EGL介绍与简单GLSurfaceView实现思路

EGL介绍与简单GLSurfaceView实现思路

  • EGL介绍与简单GLSurfaceView实现思路
    • 前言
    • EGL
      • 什么是EGL
      • 为什么用EGL
      • EGL关键数据类型
      • EGL的基础用法
      • 如何选择Surface
      • 基础API说明
      • eglSwapBuffers流程
        • 为什么Whee离屏渲染加水印不需要调用eglSwapBuffer?
    • 渲染流程
    • 示例代码
    • 相关链接

前言

本文刘老师会介绍讲下何为EGL以及简单GLSurfaceView的简单实现,旨在让大家在使用GLSurfaceView时能明白Android在OpenGL ES的使用这块为大家提供了多大的便利性,同时也能让大家了解,一个标准的GL环境是怎么创建的,GLSurfaceView总体的执行逻辑是怎么样的。

EGL

什么是EGL

我们首先来看一下什么是EGL。EGL是一个介于Khronos(制定并发布图形API的组织)渲染API和native平台底层Window系统的中间层接口,它负责图像Context的管理、Surface/渲染buffer的创建、绑定以及rendering synchronization等事情。

下图是EGL中间层的示意图。

EGL介绍与简单GLSurfaceView实现思路_第1张图片

为什么用EGL

我们先从OpenGL的绘制条件讲起。OpenGL ES的运行需要一个rendering context和一个drawing surface。其中rendering context会存储OpenGL的状态信息;drawing surface是基本图元绘制的地方,它还指定了渲染时使用哪种类型的buffer(color buffer, depth buffer, stencil buffer))。

EGL就提供了一套创建可给OpenGL等渲染API在上边绘制、创建context的surface的机制。

这套机制具体可以提供以下的功能:

  • 可以跟设备native windowing system交互的EGLDisplay
  • 查询Drawing surface相关配置信息的接口(EGL14.eglGetConfigs, EGL14.eglGetConfigAttrib等)
  • 同步不同渲染API的渲染结果(如OpenGL 和 OpenVG混合渲染的场景)
  • 管理渲染资源(如纹理映射)

另一方面,OpenGL是一套跨平台的图形API,本着单一责任原则,OpenGL只提供绘制图形的功能,因此,我们还需要一套跨平台的控制native显示设备的接口,这套接口就是EGL。

EGL关键数据类型

EGL介绍与简单GLSurfaceView实现思路_第2张图片

  • EGLDisplay: 系统显示 ID 或句柄,是对显示设备的抽象,可以理解为一个前端的显示窗口
  • EGLContext: OpenGL ES 图形上下文,它代表了OpenGL状态机,如果没有它,OpenGL指令就没有执行的环境。
  • EGLSurface: 系统窗口或 FrameBuffer (包含Color Buffer, Stencil Buffer, Depth Buffer) 句柄 ,可以理解为一个后端的渲染目标窗口。
  • EGLConfig: 创建EGLContextEGLSurface所需要的配置,一般我们会在这里配置EGLSurface Color Buffer里RGBA各个颜色值所占的位数、Stencil Buffer和Depth Buffer的位数,以及指定能绘制到surface的渲染api(OpenGL ES, OpenVG等)。

EGL的基础用法

  1. 获取与app或者显示屏关联的display
  2. 初始化display
  3. 创建surface
  4. 创建context并与display关联起来,这个context会保存OpenGL的状态
  5. 将context “make current”(EGL14.makeCurrent),后续的OpenGL操作将影响当前context的状态
  6. 使用OpenGL渲染
  7. 调用flush或者swap buffers,EGL就会告诉系统或者native window system展示渲染好的surface

下图是EGLContext, EGLDisplay, EGLConfig和EGLSurface涉及的命令

EGL介绍与简单GLSurfaceView实现思路_第3张图片

如何选择Surface

Surface实际上就是一个FrameBuffer,也就是渲染的地方。

  • 上屏渲染: EGL Window. 使用 eglCreateWindowSurface创建。在Android中eglCreateWindowSurface接口所需的参数之一EGLNativeWindow,一般建议用SurfaceTexture,至于SurfaceTexture,可以在SurfaceView或者TextureView中获取。
  • 离屏渲染: EGL Pbuffers (Pixel buffer). 使用eglCreatePbufferSurface创建。还有一个是Pixmap,PixmapSurface上绘制的图会保存在内存中,且跨平台支持并不好,而PbufferSurface绘制的图是保存在显存中的,一般建议用PbufferSurface。

基础API说明

以下函数来自类 EGL14

api 

description 

EGLDisplay eglGetDisplay(int id)  id决定了我们能获取到什么display,默认用EGL_DEFAULT_DISPLAY。如果返回结果不是EGL_NO_DISPLAY的话,则表示获取成功。 
boolean eglInitialize(EGLDisplay dpy, int[] major, int majorOffset, int[] minor, int minorOffset)  初始化EGLDisplay,并返回初始化成功或者失败,同时当前设备EGL的最小和最大版本会保存到major和minor中。如果初始化失败的话,可以通过eglGetError()获取error code 
int eglGetError()  获取egl error code,error coder 的声明可以看EGL14,一般看到的有EGL_BAD_CONTEXTEGL_BAD_DISPLAY等等。这些error code的信息可以到https://www.khronos.org/registry/EGL/specs/eglspec.1.4.pdf 查阅。 
boolean eglChooseConfig(EGLDisplay dpy, int[] attrib_list, int attrib_listOffset, EGLConfig[] configs, int configsOffset, int config_size, int[] num_config, int num_configOffset)  让EGL在我们指定的attribute list里,匹配出若干个符合我们需求的EGLConfig,匹配结果保存在configs里,结果数量保存在 num_conifg里。attribute可以在https://www.slideshare.net/Khronos_Group/egl-11查阅。 
EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, int[] surfaceAttrib, int attribOffset)  创建离屏渲染用的EGLSurface,surfaceAttribute一般传EGL_WIDTHEGL_HEIGHTEGL_RENDER_BUFFER等值。如果返回的不是EGL_NO_SURFACE的话表示创建成功。 
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext sharedContext, int[] attrib, int attribOffset)  创建EGLContext。如果不是创建共享EGLContext的话,sharedContext传EGL_NO_CONTEXT即可;attrib只接受一个参数:EGL_CONTEXT_CLIENT_VERSION,表示OpenGL ES Context的版本号。如果是OpenGL ES2就传2,3就传3。 
boolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, int attribute, int[] value, int offset);  查询EGLContext的属性值,这里只能查EGL_CONTEXT_CLIENT_VERSION,也就是ctx所用的OpenGL ES版本。 
boolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx )  EGLContextEGLSurface关联起来 
boolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface )  这个函数的命名来自于传统的屏幕front和back buffer swap更新机制。如果surface是window surface,那么这个函数会把color buffer更新到native window上(即显示渲染结果);如果是pixel buffer或者pixmap的话,eglSwapBuffers没有效果,你知道吧。 

eglSwapBuffers流程

EGL介绍与简单GLSurfaceView实现思路_第4张图片

利用双缓冲进行Swap的时候,Display和Surface进行实际意义上的地址交换,来实现eglSwapBuffers的标准, 如上图的右侧所示。 上图的左侧表示,单缓冲Framebuffer的形式,Surface永远都在后端, 显示的永远是Display,在GPU出现后已不使用。

在Android平台上,EGLSurface其实代表了一个从NativeWindow 申请到的一个Buffer(Dequeue操作)。当调用eglSwapBuffers时,对于一般应用窗口而言,NativeWindow将该Surface的Buffer 提交回去给SurfaceFlinger(Queue操作),然后又重新从NativeWindow中重新Dequeue出来一个新的Buffer给eglSurface。而eglDisplay并不代表实际的意义。我们只是从接口上感觉是,surface和display进行了交换。

为什么Whee离屏渲染加水印不需要调用eglSwapBuffer?

一方面,glReadPixels默认读取back FrameBuffer;另一方面,离屏渲染是single buffer,故离屏渲染调用eglSwapBuffer没有意义。

调用代码示例可以看本文末尾。

渲染流程

EGL介绍与简单GLSurfaceView实现思路_第5张图片

示例代码

详见OpenGLProjects develop分支下的GLViewDemo。

相关链接

https://www.khronos.org/registry/EGL/specs/eglspec.1.4.pdf

你可能感兴趣的:(Android开发,OpenGL,Shader,效果专栏)