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使用了。

相关推荐
西瓜本瓜@2 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
似霰6 小时前
安卓adb shell串口基础指令
android·adb
fatiaozhang95278 小时前
中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包
android·adb·电视盒子·魔百盒刷机·魔百盒固件
CYRUS_STUDIO9 小时前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师9 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师9 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫9 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
ta叫我小白10 小时前
实现 Android 图片信息获取和 EXIF 坐标解析
android·exif·经纬度
dpxiaolong11 小时前
RK3588平台用v4l工具调试USB摄像头实践(亮度,饱和度,对比度,色相等)
android·windows
tangweiguo0305198712 小时前
Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案
android