四、谈谈OpenGL ES中EGL的作用

3,140 阅读3分钟

本篇算是java层中关于OpenGL ES的最后一篇,前三篇文章我们具体分析了使用流程,这一篇我们将会分析EGLEGL是贯穿我们OpenGL ES一个很重要的概念,理解它的作用有助于我们更好的理解OpenGL ES


老规矩我们先看下关于EGL在源码中使用到的地方:

private static class EglHelper {
    public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
        mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
    }

    // 用给点的配置属性初始化EGL
    public void start() {
        // 获取EGL实例
        mEgl = (EGL10) EGLContext.getEGL();
        // 进入默认显示
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }
        // 为EglDisplay实例初始化EGL
        int[] version = new int[2];
        if(!mEgl.eglInitialize(mEglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed");
        }
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view == null) {
            mEglConfig = null;
            mEglContext = null;
        } else {
            mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
            // 创建EGL上下文,不过要尽可能少的创建,因为EGL上下文对象很重
            mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
        }
        // 没有上下文就要抛异常,当对应的view对象被回收就会执行
        if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
            mEglContext = null;
            throwEglException("createContext");
        }
        mEglSurface = null;
    }

    // 为当前的SurfaceHolder surface创建一个egl surface,如果已经存在就销毁重建
    public boolean createSurface() {
        // 使用这步,必须要保证egl对象,display和config都存在
        if (mEgl == null) {
            throw new RuntimeException("egl not initialized");
        }
        if (mEglDisplay == null) {
            throw new RuntimeException("eglDisplay not initialized");
        }
        if (mEglConfig == null) {
            throw new RuntimeException("mEglConfig not initialized");
        }

        // 如果窗口大小改变,我们就要新建一个surface
        destroySurfaceImp();

        // 创建一个我们能渲染的surface
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
                mEglDisplay, mEglConfig, view.getHolder());
        } else {
            mEglSurface = null;
        }
        // 如果对应view被回收或者surface创建失败,就返回创建失败
        if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
            ...
            return false;
        }
        // 在我们发出GL命令之前,我们需要确认上下文是当前上下文,并且
        // 被绑定到当前的surface
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            return false;
        }
        return true;
    }

    // 给当前EGL上下文创建一个GL对象
    GL createGL() {
        GL gl = mEglContext.getGL();
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            if (view.mGLWrapper != null) {
                gl = view.mGLWrapper.wrap(gl);
            }
            ...
        }
        return gl;
    }

    // 展示当前渲染的表面,这里很好的说明了上一篇文章的一个关键点,就是
    // 真正展示出来,并不是在onDrawFrame中
    public int swap() {
        if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
            return mEgl.eglGetError();
        }
        return EGL10.EGL_SUCCESS;
    }
    ...
    private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
    EGL10 mEgl;
    EGLDisplay mEglDisplay;
    EGLSurface mEglSurface;
    EGLConfig mEglConfig;
    EGLContext mEglContext;
}

其实这个类挺长的,我只是留了些比较关键的步骤,但是即使到了这里,我们对EGL还是很懵逼,只是知道EGL ContextEGL SurfaceEGL Display是很重要,EGL Context提供了上下文环境,最后的渲染最后都会被渲染到EGL Surface,而最终的的显示是通过EGL Display来完成的。

1. 什么是EGL?

EGL:Embedded Graphics Library,是连接OpenGL ES和本地窗口系统的接口,由于OpenGL ES是跨平台的,引入EGL就是为了屏蔽不同平台上的区别。

2. EGL的执行顺序是什么?
  • 建立本地窗口系统和OpenGL ES的连接,拿到EGLDisplay
  • 通过EGLDisplay初始化EGL,使用eglInitialize函数
  • 确定可用的渲染表面surface的配置,一旦初始化了EGL,就可以确定可用渲染表面的类型和配置了,有两种方法,一是通过eglGetConfigs、eglGetConfigAttrib,另一种就是通过eglChooseChofig
  • 创建渲染表面,有了上一步获取的EGLConfig后,就可以调用eglCreateWindowSurface来创建渲染表面。
  • 创建渲染上下文,使用eglCreateContext创建EGL渲染上下文,然后调用eglBindAPI来将此上下文绑定到当前API
  • 指定某个EGL Context为当前上下文,使用eglMakeCurrent函数进行当前上下文的绑定,因为一个程序可能创建多个EGL Context
  • 使用OpenGL相关的API进行绘制操作
  • 交换EGLSurface的内部缓冲和EGL创建的和平台无关的窗口diaplay,即调用eglSwapBuffers(mEglDisplay, mEglSurface),如果surface是一个window surface,那么该函数执行的结果将是将数据给本地窗口,即显示在屏幕上。如果surface是一个屏幕外渲染surface(pixel buffer),执行该函数没有效果。

具体的描述,可以参考EGL介绍这篇文章。

总结: 通过上面的EGL执行过程分析和源码分析,再结合之前的三篇文章,我相信对于OpenGL ES的执行过程,我们已经有了一个比较清晰的认识。