OpenGL ES 2.0 —— EGL 的应用

 

前言

通过前面的学习, 我们知道如何使用 GLSurfaceView 配合自定义 Renderer 来绘制我们想要的图像和纹理, 但却没有深究它是怎么工作的

这里我们通过 EGL 的学习来理清这一流程

什么是 EGL?

OpenGL ES 是操作 GPU 的 API, 它无法直接与屏幕设备进行交互, EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它也主要由厂商来实现

  • EGLDisplay
    • 显示设备抽象描述
  • EGLContext
    • 描述 OpenGL ES 的上下文, 存储 OpenGL ES绘图的一些状态信息
  • EGLSurface
    • 描述存储图像的内存缓冲区域, 在 Android 端为 Surface/SurfaceTexture

EGL 在 GLSurfaceView 中的使用

我们之前定义的 Renderer 能够在 GLSurfaceView 上执行渲染操作, 是因为 GLSurfaceView 中初始化了 EGL 的环境, 我们看看它是如何实现的

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
    
    private GLThread mGLThread;
    private Renderer mRenderer;
    
    public void setRenderer(Renderer renderer) {
        ......
        mRenderer = renderer;
        // 创建 GL 线程
        mGLThread = newGLThread(mThisWeakRef);
        // 启动 GL 线程
        mGLThread.start();
    }
    
}

好的, 可以看到在 GLSurfaceView.setRenderer 的方法中, 创建并启动了 GLThread 线程, 接下来看看具体实现

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {

    static class GLThread extends Thread {
    
        @Override
        public void run() {
            try {
                guardedRun();
            } catch (InterruptedException e) {
                ......
            } finally {
                ......
            }
        }
        
        private void guardedRun() throws InterruptedException {
        
            mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
            mHaveEglContext = false;
            mHaveEglSurface = false;
            mWantRenderNotification = false;

            try {
                GL10 gl = null;
                boolean createEglContext = false;
                boolean createEglSurface = false;
                boolean createGlInterface = false;
                boolean lostEglContext = false;
                boolean sizeChanged = false;
                while (true) {
                    synchronized (sGLThreadManager) {
                        while (true) {
                            if (mShouldExit) {
                                return;
                            }
                            ......
                            // Ready to draw?
                            if (readyToDraw()) {
                                // If we don't have an EGL context, try to acquire one.
                                if (! mHaveEglContext) {
                                    if (askedToReleaseEglContext) {
                                        askedToReleaseEglContext = false;
                                    } else {
                                        try {
                                            // 1. 初始化 GL 的环境
                                            mEglHelper.start();
                                        } catch (RuntimeException t) {
                                            ......
                                        }
                                        ......
                                    }
                                }
                                ......
                            } else {
                                ......
                            }
                        }
                    } 
                    ......
                    if (createEglSurface) {
                        // 2. 创建 GLSurface 描述帧缓冲
                        if (mEglHelper.createSurface()) {
                            ......
                        } else {
                            ......
                            continue;
                        }
                        createEglSurface = false;
                    }
                    ......
                    // 3. 回调 Renderer 的生命周期
                    if (createEglContext) {
                        ......
                        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                        if (view != null) {
                            try {
                                // 3.1 回调 onSurfaceCreated
                                view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
                            } finally {
                                ......
                            }
                        }
                        createEglContext = false;
                    }

                    if (sizeChanged) {
                        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                        if (view != null) {
                            try {
                                // 3.2 回调 onSurfaceChanged
                                view.mRenderer.onSurfaceChanged(gl, w, h);
                            } finally {
                                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                            }
                        }
                        sizeChanged = false;
                    }
                    ......
                    {
                        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                        if (view != null) {
                            try {
                                ......
                                // 3.3 执行 onDrawFrame
                                view.mRenderer.onDrawFrame(gl);
                                ......
                            } finally {
                                ......
                            }
                        }
                    }
                    // 4. 将 EGLSurface 中的数据推入 SurfaceFlinger
                    int swapError = mEglHelper.swap();
                    ......
                }

            } finally {
                ......
            }
        }
        
    }
}

好的, 可以看到 GLThread 的 run 方法中启动了 guardedRun 方法, 其内部维护了一个死循环, 具体的流程如下

  • 调用 mEglHelper.start 初始化 GL 的环境
  • 调用 mEglHelper.createSurface 创建缓冲帧 EGLSurface
  • 回调 Renderer 的生命周期了
  • 调用 mEglHelper.swap 将 EGLSurface 中的数据推入 SurfaceFlinger

可以看到, 之所以我们的 Renderer 能够生效, 是因为在 GLSurfaceView 的 GLThread 线程中已经初始化了 EGL 的环境

EGL 的操作封装在了 EGLHelper 这个对象内部, 接下来我们一一分析这几个步骤

一. 创建 EGL 环境

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {

    private static class EglHelper {
        /**
         * Initialize EGL for a given configuration spec.
         * @param configSpec
         */
        public void start() {
            ......
            // 获取 EGL10 对象
            mEgl = (EGL10) EGLContext.getEGL();

            // 1. 创建 EGLDisplay 描述本地窗口的连接
            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
            ......

            // 1.2 初始化 EGLDisplay, 将版本号注入
            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 {
                // 2. 创建 EGLConfig
                mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
                // 3. 创建 EGLContext
                mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
            }
            ......

            mEglSurface = null;
        }
    }
    
}

可以看到 EglHelper.start 中的流程是非常清晰, 主要有三个步骤

  • 创建 EGLDisplay 用于描述硬件屏幕
    • 初始化 EGL, 获取主次版本号信息
  • 创建 EGLConfig
  • 创建 EGLContext

一) 创建 EGLDisplay

// frameworks/native/opengl/include/EGL/egl.h
typedef void *EGLConfig;
typedef void *EGLSurface;
typedef void *EGLContext;

// frameworks/native/opengl/libagl/egl.cpp
EGLDisplay eglGetDisplay(NativeDisplayType display)
{
......
    if (display == EGL_DEFAULT_DISPLAY) {
        EGLDisplay dpy = (EGLDisplay)1;
        // 获取 EGLDisplay 对应的结构体 egl_display_t
        egl_display_t& d = egl_display_t::get_display(dpy);
        // 保存显示的类型
        d.type = display;
        // 返回 EGLDisplay
        return dpy;
    }
    return EGL_NO_DISPLAY;
}

const unsigned int NUM_DISPLAYS = 1;

struct egl_display_t
{

    NativeDisplayType  type;
    std::atomic_size_t initialized;
    // 构造
    egl_display_t() : type(0), initialized(0) { }
    
    // 创建结构体
    static egl_display_t& get_display(EGLDisplay dpy);
    
    static EGLBoolean is_valid(EGLDisplay dpy) {
        // DisplayType >= 1 说明 egl_display_t 有效
        return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE;
    }
    
};

好的, 可以看到 Native 层 EGLDisplay 是一个显示设备的标示符

  • 当传入 EGL_DEFAULT_DISPLAY 时, 默认为 1
  • EGLDisplay 对应了 egl_display_t 结构体

二) 创建 EGLConfig

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {

    private EGLConfigChooser mEGLConfigChooser;

    public void setRenderer(Renderer renderer) {
        checkRenderThreadState();
        if (mEGLConfigChooser == null) {
            mEGLConfigChooser = new SimpleEGLConfigChooser(true);
        }
    }
    
    private class SimpleEGLConfigChooser extends ComponentSizeChooser {
        public SimpleEGLConfigChooser(boolean withDepthBuffer) {
            super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);
        }
    }
    
    /**
     * Choose a configuration with exactly the specified r,g,b,a sizes,
     * and at least the specified depth and stencil sizes.
     */
    private class ComponentSizeChooser extends BaseConfigChooser {
        public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
                int alphaSize, int depthSize, int stencilSize) {
            super(
                // 1. 定义 EGLConfig 配置数组
                new int[] {
                    EGL10.EGL_RED_SIZE, redSize,
                    EGL10.EGL_GREEN_SIZE, greenSize,
                    EGL10.EGL_BLUE_SIZE, blueSize,
                    EGL10.EGL_ALPHA_SIZE, alphaSize,
                    EGL10.EGL_DEPTH_SIZE, depthSize,
                    EGL10.EGL_STENCIL_SIZE, stencilSize,
                    EGL10.EGL_NONE
                }
            );
            ......
       }
       ......
    }
    
    private abstract class BaseConfigChooser
            implements EGLConfigChooser {
        
        protected int[] mConfigSpec;

        public BaseConfigChooser(int[] configSpec) {
            // 2. 过滤配置属性
            mConfigSpec = filterConfigSpec(configSpec);
        }

        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
            // 3. 通过 EGL10.eglChooseConfig 获取符合配置的 EGLConfig 个数
            int[] num_config = new int[1];
            if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
                    num_config)) {
                ......
            }
            int numConfigs = num_config[0];
            ......
            // 4. 通过 EGL10.eglChooseConfig 获取所有符合配置的 EGLConfig
            EGLConfig[] configs = new EGLConfig[numConfigs];
            if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
                    num_config)) {
                ......
            }
            // 5. 调用了抽象方法 chooseConfig, 一般选择 EGLConfig[] 第 0 个元素
            EGLConfig config = chooseConfig(egl, display, configs);
            if (config == null) {
                ......
            }
            return config;
        }
        
    }
    
}

可以看到这个 mEGLConfigChooser 的默认实现为 SimpleEGLConfigChooser 其具体的流程如下

  • 定义 EGLConfig 配置数组
  • 过滤配置数组
  • 通过 EGL10.eglChooseConfig 获取符合配置的 EGLConfig 的个数
  • 通过 EGL10.eglChooseConfig 获取所有符合配置的 EGLConfig
  • 取所有符合配置的 EGLConfig 的首元素

有了 EGLConfig 之后便可以创建了 EGLContext 了

三) EGLContext 的创建

从上面的分析中可知, 创建 EGLContext 的动作由 mEGLContextFactory.createContext(…) 完成

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {

    private class DefaultContextFactory implements EGLContextFactory {
        private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;

        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
                    EGL10.EGL_NONE };
            // 调用了 eglCreateContext 方法
            return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
                    mEGLContextClientVersion != 0 ? attrib_list : null);
        }
    }
}

下面看看 native 的实现

EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
                            EGLContext /*share_list*/, const EGLint* /*attrib_list*/)
{
    // 验证 EGLDisplay 是否有效
    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);

    // 创建 Native 层的 EGLContext
    ogles_context_t* gl = ogles_init(sizeof(egl_context_t));
    ......

    // 创建 egl_context_t 真正描述 egl 的上下文
    egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base);
    c->flags = egl_context_t::NEVER_CURRENT;
    c->dpy = dpy;
    c->config = config;
    c->read = 0;
    c->draw = 0;
    
    // 强转成 EGLContext
    return (EGLContext)gl;
}

好的, 可以看到创建 EGLContext 的过程与创建 EGLDisplay 一致, 它对应的结构体实现为 egl_context_t

至此当前线程的 EGL 的环境便搭建好了, 我们知道 OpenGL 绘制之后是需要输出的, 这个输出的缓冲区即为 EGLSurface, 接下来看看缓冲区的相关操作

二. 创建数据缓冲区

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
    
    private static class EglHelper {
        
        public boolean createSurface() {
            ......
            /*
             * Create an EGL surface we can render into.
             */
            GLSurfaceView view = mGLSurfaceViewWeakRef.get();
            if (view != null) {
                // 1. 调用  mEGLWindowSurfaceFactory.createWindowSurface 创建 EGLSurface
                mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
                        mEglDisplay, mEglConfig, view.getHolder());
            } else {
                mEglSurface = null;
            }

            // 2. 将当前线程的 EGL 上下文设置为当前的 mEglContext
            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                ......
                return false;
            }

            return true;
        }
    }
}

可以看到 EglHelper.createSurface 中主要有两步操作

  • 创建 EGLSurface
  • 调用 EGL10.eglMakeCurrent 切换上下文
    • 上下文切换成功之后, 当前线程便可以在该上下文下执行 OpenGL ES 的 API 了

我们先看看 EGLSurface 的创建

一) EGLSurface 的创建

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
    
    public void setRenderer(Renderer renderer) {
        checkRenderThreadState();
        ......
        if (mEGLWindowSurfaceFactory == null) {
            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
        }
       ......
    }
    
    private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {

        public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
                EGLConfig config, Object nativeWindow) {
            EGLSurface result = null;
            try {
                // 调用了 EGL10.eglCreateWindowSurface 创建了 EGLSurface
                result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
            } catch (IllegalArgumentException e) {
                ......
            }
            return result;
        }
        
    }
    
}

可以看到这里调用了一个 EGL10.eglCreateWindowSurface 创建了一个 EGLSurface 对象, 接下来看看 native 的实现

EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
    return createWindowSurface(dpy, config, window, attrib_list);
}

static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
        NativeWindowType window, const EGLint* /*attrib_list*/)
{
    // 验证 EGLDisplay 是否有效
    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
    ......
    // 创建 egl_surface_t
    egl_surface_t* surface;
    surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
            static_cast<ANativeWindow*>(window));
    ......
    return surface;
}

struct egl_surface_t
{
    enum {
        PAGE_FLIP = 0x00000001,
        MAGIC     = 0x31415265
    };

    uint32_t            magic;
    EGLDisplay          dpy;
    EGLConfig           config;
    EGLContext          ctx;
    ......
}

struct egl_window_surface_v2_t : public egl_surface_t
{
    egl_window_surface_v2_t(
            EGLDisplay dpy, EGLConfig config,
            int32_t depthFormat,
            ANativeWindow* window);
    
private:
    ANativeWindow*   nativeWindow;
    ANativeWindowBuffer*   buffer;
    ANativeWindowBuffer*   previousBuffer;
    int width;
    int height;
    void* bits;
    GGLFormat const* pixelFormatTable;
    ......
}

egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
        EGLConfig config,
        int32_t depthFormat,
        ANativeWindow* window)
    : egl_surface_t(dpy, config, depthFormat),
    nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL)
{
    ......
}

好的, 我们看到 Native 层创建了一个 egl_surface_t 对象, 它的实现结构体为 egl_window_surface_v2_t, 它与 Surface/SurfaceTexture 对应

至此我们创建了一个用于保存 OpenGL 渲染管线输出数据的缓冲区, 接下来需要将其绑定到当前线程的 EGLContext, 才能正常工作, 我们看看 eglMakeCurrent 函数的实现

二) EGLSurface 绑定 EGLContext

EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
                            EGLSurface read, EGLContext ctx)
{
    ......// 验证异常信息
    
    // 获取当前线程上下文
    GLContext current_ctx = EGL_NO_CONTEXT;
     if (ctx == EGL_NO_CONTEXT) {
        // if we're detaching, we need the current context
        current_ctx = (EGLContext)getGlThreadSpecific();
    } 
    
    // 将传入的上下文置为当前线程的上下文
    ogles_context_t* gl = (ogles_context_t*)ctx;
    if (makeCurrent(gl) == 0) {
        if (ctx) {
            ......// 处理 EGLSurface 与新的上下文绑定
        } else {
            ......// 处理 EGLSurface 与上下文的解绑
        }
        return EGL_TRUE;
    }
    return setError(EGL_BAD_ACCESS, EGL_FALSE);
}


static int makeCurrent(ogles_context_t* gl)
{
    // 之前的上下文
    ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();
    if (gl) {
        // 传入的上下文
        egl_context_t* c = egl_context_t::context(gl);
        
        // 若传入的上下文已经标记为当前上下文
        if (c->flags & egl_context_t::IS_CURRENT) {
            // 当前上下文与之不等, 这说明我们传入的 c 绑定的是其他线程的上下文
            if (current != gl) {
                return -1;
            }
        } else {
            // 解除当前线程绑定的上下文
            if (current) {
                // mark the current context as not current, and flush
                glFlush();
                egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
            }
        }
        // 将传入的 c 置为当前线程的上下文
        if (!(c->flags & egl_context_t::IS_CURRENT)) {
            setGlThreadSpecific(gl);
            c->flags |= egl_context_t::IS_CURRENT;
        }
    } else {
        // 若 gl 为 null, 则意为取消当前线程上下文的绑定
        if (current) {
            glFlush();
            egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
        }
        // this thread has no context attached to it
        setGlThreadSpecific(0);
    }
    return 0;
}

好的, 可以看到 makeCurrent 切换 OpenGL 渲染线程的上下文操作还是比较复杂的

  • 传入 EGLContext 为无效, 则表示清空当前线程的上下文
    • EGLSurface 会与之解绑
  • 传入的 EGLContext 有效, 则将其置为当前线程的上下文
    • 若 EGLContext 已绑定为其他线程的上下文, 会爆出异常
    • 更新 EGLSurface 绑定的上下文

现在 EGLSurface 与 EGLContext 已经绑定好了, 现在就可以安心的执行 OpenGL 的渲染管线了, 也就是我们业务代码的编写

业务代码编写完成之后, 需要将数据推送到 SurfaceFlinger, 这就需要调用 eglSwapBuffers 来推送数据

三. 通知数据更新

public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
    
    private static class EglHelper {
    
        public int swap() {
            // 调用了 EGL10.eglSwapBuffers, 将 mEglSurface 中的数据推送给到 SurfaceFlinger
            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
                return mEgl.eglGetError();
            }
            return EGL10.EGL_SUCCESS;
        }
    }
    
}

置换数据的操作很简单, 调用了 EGL10.eglSwapBuffers, 我们看看它的实现

EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{   
    // 验证 Display 是否有效
    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
        
    // 获取 EGLSurface 对应的结构体 egl_surface_t
    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
    ......
    // 调用 egl_surface_t 的 swapBuffers
    d->swapBuffers();
    ......
    return EGL_TRUE;
}

可以看到, 这里调用了 egl_surface_t 的 swapBuffers 函数, 在 EGLSurface 创建的时候, 我们知道它的实现类为 egl_window_surface_v2_t, 因此我们看看 egl_window_surface_v2_t 的 swapBuffers 的实现

EGLBoolean egl_window_surface_v2_t::swapBuffers()
{   
    // 上面执行了渲染管线, 因此这里的 buffer 非 0 
    if (!buffer) {
        return setError(EGL_BAD_ACCESS, EGL_FALSE);
    }
    
    // 1. 从上一次重绘区域上拷贝数据
    if (!dirtyRegion.isEmpty()) {
        dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
        if (previousBuffer) {
            // 判断是否需要从上一次重绘区域上取数据
            Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
            if (!copyBack.isEmpty()) {
                // 从上一帧的缓冲区取数据, 覆盖到 buffer 中
                void* prevBits;
                if (lock(previousBuffer, 
                        GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
                    // copy from previousBuffer to buffer
                    copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
                    unlock(previousBuffer);
                }
            }
        }
        // 更新被重绘的区域
        oldDirtyRegion = dirtyRegion;
    }
    
    if (previousBuffer) {
        previousBuffer->common.decRef(&previousBuffer->common); 
        previousBuffer = 0;
    }
    // 走到这里, 说明对 buffer 的操作已经完成了, 释放锁定
    unlock(buffer);
    // 将其置为上一帧数据
    previousBuffer = buffer;
    
    // 2. 使用 nativeWindow 将这个缓冲区到 SurfaceFlinger 待绘制队列
    nativeWindow->queueBuffer(nativeWindow, buffer, -1);
    buffer = 0;

    // 3. 获取一个新的缓冲区, 保存到 buffer 中
    int fenceFd = -1;
    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) {
        // 获取用存储栅格化数据共享内存的内存文件描述符
        sp<Fence> fence(new Fence(fenceFd));
        // 等待缓冲区内存分配完毕
        if (fence->wait(Fence::TIMEOUT_NEVER) != NO_ERROR) {
            nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd);
           return setError(EGL_BAD_ALLOC, EGL_FALSE);
        }
        ......
        // reallocate the depth-buffer if needed
        if ((width != buffer->width) || (height != buffer->height)) {
            // TODO: we probably should reset the swap rect here
            // if the window size has changed
            width = buffer->width;
            height = buffer->height;
            if (depth.data) {
                free(depth.data);
                depth.width   = width;
                depth.height  = height;
                depth.stride  = buffer->stride;
                uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
                        static_cast<uint64_t>(depth.height) * 2;
                if (depth.stride < 0 || depth.height > INT_MAX ||
                        allocSize > UINT32_MAX) {
                    setError(EGL_BAD_ALLOC, EGL_FALSE);
                    return EGL_FALSE;
                }
                depth.data    = (GGLubyte*)malloc(allocSize);
                if (depth.data == 0) {
                    setError(EGL_BAD_ALLOC, EGL_FALSE);
                    return EGL_FALSE;
                }
            }
        }
        ......
    } else {
        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
    }

    return EGL_TRUE;
}

可以看到这里的 swapBuffers 的主要任务, 即将绘制到 Buffer 中的数据, 推送到 SurfaceFlinger 的渲染队列, 其主要步骤如下

  • 从上一次重绘区域上拷贝数据
  • 使用 nativeWindow(Surface/SurfaceTexture) 将这个缓冲区到 SurfaceFlinger 进程的 Layer 队列中
  • 获取一个新的缓冲区, 保存到 buffer 中

四. EGL 的销毁

前面介绍了 GLSurfaceView 渲染线程 EGL 环境的创建相关的流程, 相对的, 当线程执行完毕, 我们还需要及时的释放 EGL 占用的资源

GLSurfaceView 中对于释放资源的代码比较分散, 这里就不以 GLSurfaceView 举例了, 它们的操作如下

public void release() {
    if (mEGLDisplay != EGL10.EGL_NO_DISPLAY) {
        EGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
                EGL10.EGL_NO_CONTEXT);
        EGL.eglDestroyContext(mEGLDisplay, mEGLContext);
        EGL.eglDestroySurface(mEGLDisplay, mEGLSurface);
        EGL.eglTerminate(mEGLDisplay);
    }
    mEGLContext = EGL10.EGL_NO_CONTEXT;
    mEGLDisplay = EGL10.EGL_NO_DISPLAY;
    mEGLSurface = EGL10.EGL_NO_SURFACE;
    mEGLConfig = null;
}

释放的操作比较简单, 这里就不再进入 native 查看了

总结

EGL 依赖关系图

通过对 GLSurfaceView 中 EGL 分析, 我们得知 EGL 的使用流程如下所示

  • 初始化环境
    • 创建屏幕的描述 EGLDisplay(egl_display_t)
    • 初始化 EGL 方法, 获取 EGL 版本号相关信息
    • 创建 EGL 配置信息 EGLConfig(egl_config_t)
    • 创建 EGL 上下文 EGLContext(egl_context_t)
    • 创建缓冲帧 EGLSurface(egl_surface_t)
  • 在线程中使用 EGL
    • 通过 eglMakeCurrent 为当前线程绑定 EGLContext
    • /////////////////////////////////////////
    • 执行 OpenGL ES 的渲染管线
    • /////////////////////////////////////////
    • 交换缓冲, 将 EGLSurface 中的数据推送到 SurfaceFlinger 进程对应的 Layer 对象的队列中
      • 推送之后, 会立即获取一个新的缓冲
  • 绘制结束后销毁当前线程的 EGL 数据

掌握了这个流程, 我们可以封装一套 EGL 通用的代码(Google 示例), 便于在更多的场景下创建 EGL, 使用 OpenGL ES 的 API

  • 感兴趣的同学可以仿照 GLSurfaceView 实现一个 GLTextureView