Android EGL 一、 初始化配置

最近在看 滤镜绘制的流程, 先把 egl 的api 写一遍,再详细写一遍流程

一、概念

 EGL : 是渲染 API(如 OpenGL ES)和原生窗口系统之间的接口, 与 opengl 对接操作GPU 能力,android使用的是 openGL ES

EGL10 11  14

 EGL10 egl = (EGL10) EGLContext.getEGL();
    EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); //获取显示设备
   
    // Init
    int[] version = new int[2];
    egl.eglInitialize(display, version); //version中存放EGL 版本号,int[0]为主版本号,int[1]为子版本号

    String vendor = egl.eglQueryString(display, EGL10.EGL_VENDOR);
    WLog.d("egl vendor: " + vendor); // 打印此版本EGL的实现厂商

    String version = egl.eglQueryString(display, EGL10.EGL_VERSION);
    WLog.d("egl version: " + version);// 打印EGL版本号

    String extension = egl.eglQueryString(display, EGL10.EGL_EXTENSIONS);
    WLog.d("egl extension: " + extension); //打印支持的EGL扩展

1,虽然Android使用(实现)的是EGL 1.4(从打印的版本号中可见), 但在Android 4.2(API 17)以前的版本没有EGL14,只有EGL10和EGL11,而这两个版本是不支持OpengGL ES 2.x的,因此在老版本中某些ES 2.x相关的常量参数只能用手写的硬编码代替,典型的如设定EGL渲染类型API的参数EGL10.EGL_RENDERABLE_TYPE,这个属性用不同的赋值指定的不同的渲染API,包括OpenGL,OpenGL ES 1.x, OpenGL ES 2.x,OpenVG等,如果采用ES 2.0,应该设置此值为: EGL14.EGL_OPENGL_ES2_BIT,但是在Android 4.2之前,没有EGL14接口,只能采取手写的硬编码来指定,类似: EGL_RENDERABLE_TYPE = 4;

2,egl.eglQueryString()用来查询EGL的相关信息,详见这里:http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/

3,EGL10.EGL_DEFAULT_DISPLAY 默认对应手机主屏幕。

 

 

OpenGL: 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令 OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态,但是当涉及到与本地窗口系统进行交互时,就需要这么一个中间层,且它最好是与平台无关的。

二、api

2.1 创建本地系统与 OpenGL es的链接, 返回 EGLDisplay 对象,  通俗讲:就是获取显示窗口

 

EGLDisplay指的是物理的显示设备比如我们的手机屏幕,我们可以通过传入屏幕设备的id去获取到设备句柄,绝大多数情况下我们传入EGL14.EGL_DEFAULT_DISPLAY获取默认的屏幕就好,一般情况下我们的手机也只有一个屏幕

拿不到设备就会返回EGL_NO_DISPLAY

EGL14.EGL_DEFAULT_DISPLAY : 默认显示,既窗口

//用于获取一个显示连接,其中display_id即要连接的display的标识
EGLDisplay eglDisplay(EGLNativeDisplayType displayId);

返回值 : EGL14.EGL_NO_DISPLAY: 没有获取到显示链接
         EGL14.EGL_BAD_DISPLAY :没有指定有效的 display
      

 

2.2 初始化环境

//用于初始化显示连接,major/minor是一对输出参数,用于返回EGL的版本号。如果不关心,可以传NULL

EGLBoolean eglInitialize(EGLDisplay display, // 创建步骤时返回的对象
                         EGLint *majorVersion, // 返回 EGL 主版本号
                         EGLint *minorVersion); // 返回 EGL 次版本号

返回  EGLBoolean:
        

2.3 初始化 EGL完成,开始配置, 配置完成后就可以渲染Surface了

EGLDisplay支持的配置有很多种,例如颜色可能支持ARGB888、RGB888、RGB444、RGB565等,我们可以通过eglGetConfigs拿到EGLDisplay支持的所有配置,然后选择我们需要的。

如果直接去遍历所有的配置找我们需要的那个,代码写起来比较麻烦。

所以EGL提供了一个eglChooseConfig方法,我们输入关心的属性,其他的属性让EGL自己匹配就好。可能会匹配出多个EGLConfig,这个时候随便选一个都可以:

        //获取所有配置
	EGLBoolean eglGetConfigs(EGLDisplay display, // 指定显示的连接
                         EGLConfig *configs, // 指定 GLConfig 列表
                         EGLint maxReturnConfigs, // 最多返回的 GLConfig 数
                         EGLint *numConfigs); // 实际返回的 GLConfig 数
 
        //查询指定配置
        EGLBoolean eglGetConfigAttrib(EGLDisplay display, // 指定显示的连接
                          EGLConfig config, // 指定要查询的 GLConfig
                          EGLint attribute, // 返回特定属性
                          EGLint *value); // 返回值

         //让EGL 选择配置
         EGLBoolean eglChooseChofig(EGLDispay display, // 指定显示的连接
                          const EGLint *attribList, // 指定 configs 匹配的属性列表,可以为 NULL
                          EGLConfig *config,   // 调用成功,返会符合条件的 EGLConfig 列表
                          EGLint maxReturnConfigs, // 最多返回的符合条件的 GLConfig 数
                          ELGint *numConfigs );  // 实际返回的符合条件的 EGLConfig 数



-------------------- 构造需要的特性列表 ------------------------------------
int[] attributes = new int[] { 
        EGL10.EGL_RED_SIZE, 8,  //指定RGB中的R大小(bits)
        EGL10.EGL_GREEN_SIZE, 8, //指定G大小
        EGL10.EGL_BLUE_SIZE, 8,  //指定B大小
        EGL10.EGL_ALPHA_SIZE, 8, //指定Alpha大小,以上四项实际上指定了像素格式
        EGL10.EGL_DEPTH_SIZE, 16, //指定深度缓存(Z Buffer)大小
        EGL10.EGL_RENDERABLE_TYPE, 4, //指定渲染api类别, 如上一小节描述,这里或者是硬编码的4,或者是EGL14.EGL_OPENGL_ES2_BIT 
        EGL10.EGL_NONE };  //总是以EGL10.EGL_NONE结尾

 

demo


int[] configNum = new int[1];
    //获取满足attributes的config个数。
    egl.eglChooseConfig(display, attributes, null, 0, configNum);
    int num = configNum[0];
    if(num != 0){
        EGLConfig[] configs = new EGLConfig[num];
        //获取所有满足attributes的configs
        egl.eglChooseConfig(display, attributes, configs, num, configNum);
        config = configs[0]; //以某种规则选择一个config,这里使用了最简单的规则。      
    }


(1)eglChooseConfig(display, attributes, configs, num, configNum); 用于获取满足attributes的所有config,
parm 1、2
parm3:存放输出的configs,
parm4:指定最多输出多少个config,
parm5由EGL系统写入,表明满足attributes的config一共有多少个。
     如果使用eglChooseConfig(display, attributes, null, 0, configNum)这种形式调用,则会在configNum中输出所有满足条件的config个数。

(2)一般习惯是获取所有满足attributes的config个数,再据此分配存放config的数组,获取所有config,根据某种特定规则,从中选择其一。

(3)API详细说明和所有可指定的attributes见这里:http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/

 打印config中的常用attributes:

/**
   * 打印EGLConfig信息
   * 
   * @param egl
   * @param display
   * @param config
   *          : 指定的EGLConfig
   */
  public static void printEGLConfigAttribs(EGL10 egl, EGLDisplay display, EGLConfig config) {
    int value = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, -1);
    WLog.d("eglconfig: EGL_RED_SIZE: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, -1);
    WLog.d("eglconfig: EGL_GREEN_SIZE: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, -1);
    WLog.d("eglconfig: EGL_BLUE_SIZE: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, -1);
    WLog.d("eglconfig: EGL_ALPHA_SIZE: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, -1);
    WLog.d("eglconfig: EGL_DEPTH_SIZE: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_RENDERABLE_TYPE, -1);
    WLog.d("eglconfig: EGL_RENDERABL_TYPE: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_SAMPLE_BUFFERS, -1);
    WLog.d("eglconfig: EGL_SAMPLE_BUFFERS: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_SAMPLES, -1);
    WLog.d("eglconfig: EGL_SAMPLES: " + value);

    value = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, -1);
    WLog.d("eglconfig: EGL_STENCIL_SIZE: " + value);
  }


 /**
   * 在指定EGLConfig中查找指定attrib的值,如果没有此属性,返回指定的默认值
   * 
   * @param egl
   * @param display
   * @param config
   *          : 指定的EGLConfig
   * @param attribute
   *          : 指定的attrib
   * @param defaultValue
   *          : 查找失败时返回的默认值
   * @return: 查找成功,返回查找值;查找失败,返回参数中指定的默认值
   */
  static public int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
      int attribute, int defaultValue) {
    int[] val = new int[1];
    if (egl.eglGetConfigAttrib(display, config, attribute, val)) {
      return val[0];
    }
    return defaultValue;
  }
复制代码

 

2.4 创建上下文,上下文包含了操作所需的所有状态信息 必须有一个可用的上下文才能进行绘图。

EGLContext是OpenGL的线程相关上下文环境,我们在OpenGL中创建的数据如图片、顶点、着色器等最后获取到的只是一个id,它的具体内容其实依赖这个EGLContext

PS: 上下文环境是线程相关的,一般来讲OpenGL的操作都在同一个线程中进行,但是有些复杂的业务场景可能需要多线程,于是可以在eglCreateContext的第三个参数里面传入share_context做到多线程共享。如果不需要多线程共享的话传入EGL14.EGL_NO_CONTEXT就好

EGLContext eglCreateContext(EGLDisplay display, // 指定显示的连接
                            EGLConfig config, // 前面选好的 EGLConfig
                            EGLContext shareContext, // 允许其它 EGLContext 共享数据,使用 EGL_NO_CONTEXT 表示不共享
                            const EGLint* attribList); // 指定操作的属性列表,只能接受一个属性 EGL_CONTEXT_CLIENT_VERSION




-------demo---------------------------------------------------------
int attrs[] = { 
        EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, 
        EGL10.EGL_NONE, };
EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrs);

---------------------------------------------------------------------

share_context: 是否有context共享,共享的contxt之间亦共享所有数据。EGL_NO_CONTEXT代表不共享。

attrib_list: 目前可用属性只有EGL_CONTEXT_CLIENT_VERSION, 1代表OpenGL ES 1.x, 2代表2.0。同样在Android4.2之前,没有EGL_CONTEXT_CLIENT_VERSION这个属性,只能使用硬编码0x3098代替。

函数详细描述:http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/

 

2.5 建渲染区域 Surface ( 获取显存),根据 2.3的配置创建,

指定一个EGLSurface告诉OpenGL应该往哪里画东西。

eglCreateWindowSurface创建EGLSurface然后用eglMakeCurrent指定OpenGL绘制的结果最后输出到这个EGLSurface上。

EGLSurface eglCreateWindowSurface(EGLDisplay display, // 指定显示的连接
                                  EGLConfig config, // 符合条件的 EGLConfig
                                  EGLNatvieWindowType window, // 指定原生窗口
                                  const EGLint *attribList); // 指定窗口属性列表,可为 NULL

 失败返回: EGL_NO_SURFACE
 EGL_BAD_MATCH :提供了与窗口属性不匹配的 EGLConfig,或该 EGLConfig 不支持渲染到窗口
 EGL_BAD_CONFIG :提供的 EGLConfig 没有得到系统支持
 EGL_BAD_NATIVE_WINDOW :提供的原生窗口句柄无效
 EGL_BAD_ALLOC :无法为新的窗口分配资源,或已经有和提供的原生窗口关联的 EGLConfig

 

详细的参数说明见这里:http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/


 其中一个attribute是EGL_RENDER_BUFFER, 用于描述渲染buffer(所有的绘制在此buffer中进行)类别,取值为EGL_SINGLE_BUFFER以及默认的EGL_BACK_BUFFER,前者属于单缓冲,绘制的同时用户即可见;后者属于双缓冲,前端缓冲用于显示,OpenGL ES 在后端缓冲中进行绘制,绘制完毕后使用eglSwapBuffers()交换前后缓冲,用户即看到在后缓冲中的内容,如此反复。其他attributes见官方文档。

public EGLSurface createEGLSurface(Surface surface) {
        int[] attribList = {
                EGL14.EGL_NONE
        };
        return EGL14.eglCreateWindowSurface(
                mEGLDisplay,
                mEGLConfig,
                surface,
                attribList,
                0);
    }

2.6 将 关联 Surface 和 Context , 这样就可以绘制数据到 Surface中了

EGLBoolean eglMakeCurrent(EGLDisplay display, // 指定显示的连接
                          EGLSurface draw, // EGL 绘图表面
                          EGLSurface read, // EGL 读取表面
                          EGLContext context); // 指定连接到该表面的上下文

 PS: 每进行相关操作完后, 最好进行一下 检测, 检测当前GL环境是否正常, 如果不正常需要及时退出‘’

EGLint eglGetError();     返回 EGL_SUCCESS 说明成功

  

2.7  eglSwapBuffers 交换缓冲区,将绘制结果推到native window上

EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);

 

 

OpenGL ES应用的大致工作过程为:

获取显示连接、初始化显示连接、创建上下文、创建窗口表面、进行初始化操作(设置OpenGL ES选项、加载贴图、创建Shader、构建绘图模型等)、进入消息循环(处理消息、进行绘制)、退出消息循环、进行资源清理、销毁窗口表面、销毁上下文、关闭显示连接;

 

 

 

三 demo

EGL14

   private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
   private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
   private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
   private EGLContext mParentContext = EGL14.EGL_NO_CONTEXT;


   private void eglSetup() {

      
	mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
	if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
	  throw new RuntimeException("unable to get EGL14 display");
	}

	int[] version = new int[2];
	if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
	  throw new RuntimeException("unable to initialize EGL14");
	}

	// Configure EGL for recording and OpenGL ES 2.0.
	int[] attribList = {
	EGL14.EGL_RED_SIZE, 8, 
	EGL14.EGL_GREEN_SIZE,8, 
	EGL14.EGL_BLUE_SIZE, 8, 
	EGL14.EGL_RENDERABLE_TYPE,
	EGL14.EGL_OPENGL_ES2_BIT, EGL_RECORDABLE_ANDROID, 1,
	EGL14.EGL_NONE };

    /** int[] attribList = {
            EGL14.EGL_BUFFER_SIZE, 32,
            EGL14.EGL_ALPHA_SIZE, 8,
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
            EGL14.EGL_NONE
        };**/


	EGLConfig[] configs = new EGLConfig[1];
	int[] numConfigs = new int[1];
	EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0,
	configs.length, numConfigs, 0);
	checkEglError("eglCreateContext RGB888+recordable ES2");

	// Configure context for OpenGL ES 2.0.
	int[] attrib_list = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,EGL14.EGL_NONE };
	mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0],
	mParentContext, attrib_list, 0);
	checkEglError("eglCreateContext");

	// Create a window surface, and attach it to the Surface we
	// received.
	int[] surfaceAttribs = { EGL14.EGL_NONE };
	mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0],
	mSurface, surfaceAttribs, 0);
	checkEglError("eglCreateWindowSurface");
  }

 egl10

mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
} else {
    Log.v(TAG, "EGL version: " + version[0] + '.' + version[1]);
}
int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
mEglConfig = chooseConfig(mEgl, mEglDisplay);
mEglContext = mEgl.eglCreateContext(
mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attribList);

if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
throw new RuntimeException("failed to createContext");
}
mEglSurface = mEgl.eglCreateWindowSurface(
mEglDisplay, mEglConfig, target, null);
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
throw new RuntimeException("failed to createWindowSurface");
}

if (!mEgl.eglMakeCurrent(
mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("failed to eglMakeCurrent");
}

mGl = (GL10) mEglContext.getGL();

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空白的泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值