OpenGL ES 之EGL(6)

OpenGL ES 之EGL(6)

简述

EGL是OpenGL ES的封装,目的是跨设备跨平台,隔离不同平台对窗口不同的实现。上一节我们基本没有使用到EGL,因为GLSurfaceView帮助我们处理了相关的逻辑,我们这一节来看一下EGL的一些概念以及接口的使用。

同时我们会介绍GLSurfaceView做了什么,是怎么配置EGL等。

EGL接口

  • 1.eglGetDisplay
    用于获取EGLDisplay,这里会关联原生窗口,EGLDisplay是对设备的抽象。
  • 2.eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion)
    初始化函数,第一个参数是eglGetDisplay返回值。
  • 3.eglChooseConfig
    EGL会根据设备配置选择合适的Config
  • 4.eglCreateWindowSurface
    通过前面EGLDisplay和EGLConfig创建EGLSurface
  • 5.eglCreateContext
    创建EGLContext,创建渲染上下文
  • 6.eglMakeCurrent
    绑定EGLContext,EGLSurface,EGLDisplay,之后即可调用openGL ES的api做图像渲染了。
  • 7.eglSwapBuffers
    交换缓冲区,调用后就会将内存中的图像显示到屏幕上。

GLSurfaceView流程

setRenderer

配置了Renderer之后,GLSurfaceView启动了一个GLThread线程

public void setRenderer(Renderer renderer) {
    checkRenderThreadState();
    if (mEGLConfigChooser == null) {
        mEGLConfigChooser = new SimpleEGLConfigChooser(true);
    }
    if (mEGLContextFactory == null) {
        mEGLContextFactory = new DefaultContextFactory();
    }
    if (mEGLWindowSurfaceFactory == null) {
        mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
    }
    // 构造并启动了一个GLThread线程
    mGLThread = new GLThread(renderer);
    mGLThread.start();
}

GLThread

调用了guardedRun。

guardedRun通过一个EglHelper来调用EGL的接口。

guardedRun在一个死循环中,死循环中还有一个死循环,这里会通过mEglHelper.start来初始化EGL。在EGLSurface创建好后,就会跳出这个死循环,在外层循环后面的逻辑,首次会通过createSurface创建EGLSurface,并且回调Renderer.onSurfaceCreated,也会检查sizeChanged,如果sizeChanged则会回调Renderer.onSurfaceChanged。

每次循环都会回调Renderer.onDrawFrame,在回调onDrawFrame之后会调用mEglHelper.swap来执行交换区。

这里EglHelper的start/createSurface/swap,我们接下来看看这几个方法。

private class GLThread extends Thread {
    // ...
    public void run() {
        setName("GLThread " + getId());
        if (LOG_THREADS) {
            DebugLog.i("GLThread", "starting tid=" + getId());
        }

        try {
            guardedRun();
        } catch (InterruptedException e) {
            // fall thru and exit normally
        } finally {
            sGLThreadManager.threadExiting(this);
        }
    }
}

private void guardedRun() throws InterruptedException {
    mEglHelper = new EglHelper();
    // ...
    try {
        // ...
        while (true) {
            synchronized (sGLThreadManager) {
                while (true) {
                    // ...
                    if ((! mHasSurface) && (! mWaitingForSurface)) {
                        if (LOG_SURFACE) {
                            DebugLog.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
                        }
                        if (mHaveEglSurface) {
                            stopEglLocked();
                        }
                        mWaitingForSurface = true;
                        sGLThreadManager.notifyAll();
                    }
                    // ...
                    // Ready to draw?
                    if ((!mPaused) && mHasSurface
                        && (mWidth > 0) && (mHeight > 0)
                        && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {

                        if (mHaveEglContext && !mHaveEglSurface) {
                    		// 检测EGL上下文
                    		if (!mEglHelper.verifyContext()) {
                    			mEglHelper.finish();
                    			mRenderer.onSurfaceLost();
                    			mHaveEglContext = false;
                    		}
                    	}

                        if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
                        	mHaveEglContext = true;
                            // 启动EGLHelper.start,这里会做EGL的初始化
                            mEglHelper.start();
                            
                            sGLThreadManager.notifyAll();
                        }
                        // ...

                        if (mHaveEglSurface) {
                            // ... 配置宽高
                            break;
                        }
                    }

                    sGLThreadManager.wait();
                }
            } // end of synchronized(sGLThreadManager)

            if (event != null) {
                event.run();
                event = null;
                continue;
            }

            if (mHasFocus) {
                if (createEglSurface) {
                    // 调用createSurface,初始化EGL上下文
                    gl = (GL10) mEglHelper.createSurface(getHolder());
                    // ...
                    // 回调Renderer.onSurfaceCreated
                    mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
                    createEglSurface = false;
                    framesSinceResetHack = 0;
                }
                    

                if (sizeChanged) {
                    // ...
                    // 回调Renderer.onSurfaceChanged
                    mRenderer.onSurfaceChanged(gl, w, h);
                    sizeChanged = false;
                }
                // ...
                    
                mWatchDog.reset();
                // 回调Renderer.onDrawFrame
                mRenderer.onDrawFrame(gl);
                    
                framesSinceResetHack++;
                // 调用eglSwapBuffers,交换缓冲区上屏显示
                if(!mEglHelper.swap()) {
                    // ...
                        
                    stopEglLocked();
                }
                
            }
            if (wantRenderNotification) {
                doRenderNotification = true;
            }
        }
            
    } finally {
    	// ... 释放EGL上下文
    }
}

EglHelper

EglHelper就是对EGL对接口进行封装,这些EGL的接口作用在前面都介绍过了。

public void start(){
    mEgl = (EGL10) EGLContext.getEGL();

    // 通过eglGetDisplay获取EglDisplay
    mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    // ...
    int[] version = new int[2];
    // 调用eglInitialize进行初始化
    if(!mEgl.eglInitialize(mEglDisplay, version)) {
        throw new RuntimeException("eglInitialize failed");
    }
    // 调用eglChooseConfig获取EglConfig
    mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
    // 创建EglContext
    mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
    if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
        throwEglException("createContext");
    }

    mEglSurface = null;
}

public GL createSurface(SurfaceHolder holder) {
    // 如果之前创建过EglSurface,直接调用eglMakeCurrent进行关联
    if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
                EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
        mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
    }
    // 调用createWindowSurface创建EglSurface  
    mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
            mEglDisplay, mEglConfig, holder);

    if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
        throwEglException("createWindowSurface");
    }

    // 调用eglMakeCurrent关联
    if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
        throwEglException("eglMakeCurrent");
    }

    GL gl = mEglContext.getGL();
    if (mGLWrapper != null) {
        gl = mGLWrapper.wrap(gl);
    }

    // ... 配置debug相关flag
    return gl;
}

public boolean swap() {
    // 调用eglSwapBuffers交换Buffer显示
    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);

    return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
}

小结

EGL的接口比较简单,流程也基本是固定的,我们以GLSurfaceView为例介绍了它的使用流程,GLSurfaceView就是启动一个线程,除了处理固定的EGL上下文初始化,还控制了Renderer回调的几个生命周期。

介绍完EGL后,我们后面就可以专注于OpenGL ES的api使用了。

相关推荐
Estar.Lee21 分钟前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯1 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey2 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!4 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟5 小时前
Android音频采集
android·音视频
小白也想学C6 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程6 小时前
初级数据结构——树
android·java·数据结构
闲暇部落8 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX10 小时前
Android 分区相关介绍
android
大白要努力!11 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle