当前位置: 首页 > article >正文

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


http://www.kler.cn/a/330067.html

相关文章:

  • ubuntu下怎么设置机器程序开机自启?
  • Python Web 开发的路径管理艺术:FastAPI 项目中的最佳实践与问题解析20241119
  • AcWing 1234. 倍数问题(周二)
  • 网络安全:我们的安全防线
  • 视觉SLAM相机——单目相机、双目相机、深度相机
  • Flutter在MaterialApp中的builder初始化多个包
  • 学生课堂行为检测数据集 8800张 上课行为 标注voc yolo 7类
  • [Go语言快速上手]函数和包
  • 在Kali Linux中使用VNC和iptables配置xrdp以实现远程连接
  • 一文上手SpringSecurity【七】
  • OpenGL笔记之事件驱动设计将相机控制类和应用程序类分离
  • 三、数据链路层(上)
  • Spring Boot与GraphQL:现代化API设计
  • 《Ubuntu20.04环境下的ROS进阶学习7》
  • Windows 10再次成为Steam上最受欢迎的操作系统 Linux用户比例略有下降
  • Redis:初识Redis
  • 【git】提交更改到仓库
  • 让CSS flex布局最后一行列表左对齐的N种方法
  • fastAPI教程:路由操作及HTTP请求响应
  • python的几个基本数据类型及其相关操作(字符串str,元组tuple,列表list,字典dict)
  • ros2 自定义工作空间添加source
  • k8s架构,从clusterIP到光电半导体,再从clusterIP到企业管理
  • 微信小程序实战教程:如何使用map组件实现地图功能
  • TCP/UDP初识
  • 物联网智能项目探究和方案设计
  • 叶国富“推翻”马云新零售,零售新王此刻登基?