EGL简介

一、什么是EGL

EGL用于管理绘图表面, 提供了如下机制:

  1. 与设备的原生窗口系统通信;
  2. 查询绘制表面的可用类型和配置;
  3. 创建绘图表面;
  4. 在OpenGL ES3.0 和其他图形渲染API直接同步渲染;
  5. 管理纹理贴图等渲染资源;

总之:EGL提供了OpenGL ES3.0和运行于计算机上的原生窗口系统之间的一个“结合”层次。使用过程可分如下几个步骤:

  1. 初始化EGL
  2. 确定可用表面配置
  3. 创建渲染窗口
  4. 创建一个渲染上下文
  5. 设置为当前的渲染环境

二、初始化EGL

EGL10 egl10 =  (EGL10) EGLContext.getEGL();
EGLDisplay eglDisplay = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (EGL10.EGL_NO_DISPLAY == eglDisplay){
   //error msg         
    return;
}
int version[] = new int[2];
if(!egl10.eglInitialize(eglDisplay, version)){
   //error msg
   return;
}

eglGetDisplay方法用于获得EGLDisplay, EGLDisplay为显示设备。获取成功后即可调用eglInitialize初始化EGL, 其中version[0]为主版本号,version[1]为子版本号。
另外也可以调用eglQueryString来查询EGL的相关信息,如版本号、实现厂家等。
例如:

String vendor = egl10.eglQueryString(eglDisplay, EGL10.EGL_VENDOR);
System.out.println("egl vendor: " + vendor); // 打印此版本EGL的实现厂商

三、确定可用表面配置

初始化EGL之后,就可以确定可用的渲染表面的类型和配置,有两种方法:

  1. 查询每个表面配置, 找出最好的选择;
  2. 指定一组需求, 让EGL推荐最佳匹配;

1、 查询所有配置

EGLConfig包含EGL相关的各种属性,如何获取EGL支持的所有配置呢?需要用到的函数为:

 boolean  eglGetConfigs(EGLDisplay display, EGLConfig[] configs, int config_size, int[] num_config);

其中:
config_size为获取EGLConfig的数量,num_config为系统支持的所有EGLConfig数量。函数有两种使用方式:第一种是configs设为null,则num_config可返回可用配置的数量;第二种方式是将config_size设置为configs数组的大小,函数将返回config_size个可用配置。为了谨慎起见使用如下:

 int configsNum[] = new int[1];
 egl10.eglGetConfigs(eglDisplay, null, 0, configsNum);

 EGLConfig eglConfig[] = new EGLConfig[configsNum[0]];
 egl10.eglGetConfigs(eglDisplay, eglConfig, configsNum[0], configsNum);
    

获取EGLConfig后可用eglGetConfigAttrib查询所支持的配置信息,如查询第i个EGLConfig中颜色缓冲区中所有颜色分量的位数:

int colorBufferSize[] = new int[1];
egl10.eglGetConfigAttrib(eglDisplay, eglConfig[i], EGL10.EGL_BUFFER_SIZE, colorBufferSize);
System.out.println("EGL_BUFFER_SIZE:" + colorBufferSize[0]);
      

2、让EGL选择配置

使用函数:

boolean  eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config);
  

可匹配符合需要的EGLConfig。
其中:attrib_list为匹配的熟悉列表。
如果匹配成功则返回true。使用方式如下:

int[] attributes = new int[] {
                EGL10.EGL_RED_SIZE, 5,  //指定RGB中的R大小(bits)
                EGL10.EGL_GREEN_SIZE, 6, //指定G大小
                EGL10.EGL_BLUE_SIZE, 5,  //指定B大小
                EGL10.EGL_DEPTH_SIZE, 1, //指定深度缓存(Z Buffer)大小
                EGL10.EGL_NONE };
int chooseNum = 10;
EGLConfig chooseConfigs[] = new EGLConfig[chooseNum];
int chooseMaxNum[] = new int[1];
egl10.eglChooseConfig(eglDisplay, attributes,  chooseConfigs, chooseNum, chooseMaxNum);

熟悉列表最后以EGL10.EGL_NONE结尾。

四、创建渲染窗口

当找到符合渲染需求的EGLConfig后,就可以创建显然窗口, 调用函数如下:

EGLSurface  eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);

五、创建一个渲染上下文

 EGLContext  eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);
  

其中share_context表示是否有context共享,共享的contxt之间亦共享所有数据,EGL_NO_CONTEXT代表不共享;attrib_list表示可用属性,当前只有EGL_CONTEXT_CLIENT_VERSION, 1代表OpenGL ES 1.x, 2代表2.0。

六、设置为当前的渲染环境

boolean  eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
    

其中:draw为绘图表面, read为读取表面。

七、demo

在Android中使用OpenGL ES一般都是直接利用GLSurfaceView控件, 其封装了EGL相关的操作,方便使用。那如何在SurfaceView中使用OpenGL ES呢? 例子如下:

  • MainActivity:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{
    private EGLUtils eglUtils;
    private SurfaceView view;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        view = new SurfaceView(this);
        view.getHolder().addCallback(this);
        setContentView(view);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        eglUtils = new EGLUtils();
        if(eglUtils.CreateEGLEnv(holder)){
            Triangle triangle = new Triangle();
            triangle.draw();
            eglUtils.swapBuffer();
            eglUtils.destroy();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
}

将EGL相关操作封装如下:

  • EGLUtils:
import android.view.SurfaceHolder;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;

public class EGLUtils {
    private EGLDisplay eglDisplay;
    private EGL10  egl10;
    private EGLSurface eglSurface;
    private EGLContext eglContext;

    public boolean CreateEGLEnv(SurfaceHolder holder){
        egl10 =  (EGL10) EGLContext.getEGL();
        eglDisplay = egl10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (EGL10.EGL_NO_DISPLAY == eglDisplay){
            System.out.println("egl 不可用:err:" + egl10.eglGetError());
            return false;
        }
        int version[] = new int[2];
        if(!egl10.eglInitialize(eglDisplay, version)){
            //error msg
            return false;
        }
        int[] attributes = new int[] {
                EGL10.EGL_RED_SIZE, 5,  //指定RGB中的R大小(bits)
                EGL10.EGL_GREEN_SIZE, 6, //指定G大小
                EGL10.EGL_BLUE_SIZE, 5,  //指定B大小
                EGL10.EGL_DEPTH_SIZE, 1, //指定深度缓存(Z Buffer)大小
                EGL10.EGL_NONE };

        int chooseNum = 10;
        EGLConfig chooseConfigs[] = new EGLConfig[chooseNum];
        int chooseMaxNum[] = new int[1];
        egl10.eglChooseConfig(eglDisplay, attributes,  chooseConfigs, chooseNum, chooseMaxNum);
        System.out.println("chooseMaxNum:" + chooseMaxNum[0]);


        eglSurface = egl10.eglCreateWindowSurface(eglDisplay, chooseConfigs[0], holder, null);
        if(eglSurface == EGL_NO_SURFACE){
            System.out.println("create surface failed,msg:" + egl10.eglGetError());
            return false;
        }
        int[] contextAttr = new int[]{
                EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL10.EGL_NONE
        };
        eglContext = egl10.eglCreateContext(eglDisplay, chooseConfigs[0], egl10.EGL_NO_CONTEXT, contextAttr);
        if(eglContext == EGL_NO_CONTEXT){
            System.out.println("create context failed,msg:" + egl10.eglGetError());
            return false;
        }
        if(!egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)){
            System.out.println("eglMakeCurrent ailed, msg:" + egl10.eglGetError());
            return false;
        }
        return true;
    }

    public void destroy(){
        egl10.eglMakeCurrent(eglDisplay, EGL_NO_SURFACE,
                EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        egl10.eglDestroySurface(eglDisplay, eglSurface);
        egl10.eglDestroyContext(eglDisplay, eglContext);
        egl10.eglTerminate(eglDisplay);
    }

    public  void swapBuffer(){
        egl10.eglSwapBuffers(eglDisplay, eglSurface);
    }
}

demo以绘制一个三角形为目的,三角形如下:

  • Triangle.java
import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;

public class Triangle {
    private FloatBuffer vertexBuffer;
    private static final float VERTEXS[] = {
            -0.5f, 0.0f,
            0.5f, 0.0f,
            0.0f, 0.5f
    };
    private static final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "gl_Position = vPosition;" +
                    "gl_PointSize = 10.0;" +
                    "}";

    private static final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "gl_FragColor = vColor;" +
                    "}";

    private int programId;
    private int vertexPositionId;
    private int colorPositionId;

    public  Triangle(){
        ByteBuffer vertexBytes = ByteBuffer.allocateDirect(
                VERTEXS.length * 4);
        vertexBytes.order(ByteOrder.nativeOrder());

        vertexBuffer = vertexBytes.asFloatBuffer();
        vertexBuffer.put(VERTEXS);
        vertexBuffer.position(0);

        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        programId = OpenGlUtils.loadProgram(vertexShaderCode, fragmentShaderCode);
    }

    public void draw(){
        GLES20.glClear(GL_COLOR_BUFFER_BIT);
        GLES20.glUseProgram(programId);
        vertexPositionId = GLES20.glGetAttribLocation(programId, "vPosition");
        colorPositionId = GLES20.glGetUniformLocation(programId, "vColor");
        GLES20.glVertexAttribPointer(vertexPositionId, 2,
                GLES20.GL_FLOAT, false,
                0, vertexBuffer);
        GLES20.glEnableVertexAttribArray(vertexPositionId);
        GLES20.glUniform4f(colorPositionId, 1.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        GLES20.glDisableVertexAttribArray(vertexPositionId);
    }
}

其中OpenGlUtils在文章2. 第一个三角形

说明:本demo只是为了演示如果在SurfaceView中使用OpenGL ES,实际中如何使用可参考:Android example source code file (GLView.java)

参考:

1. 学习OpenGL-ES: 2 - EGL解析

2. OpenGL ES 3.0编程指南

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容