Open GL ES -> SurfaceView + 自定义EGL实现OpenGL渲染框架

SurfaceView + 自定义EGL实现OpenGL渲染

Android开发中,当需要灵活控制OpenGL渲染或在多个Surface间共享EGL上下文时,自定义EGL环境是必要的选择

核心实现流程

kotlin 复制代码
+--------------------+     +--------------------+     +--------------------+
| 1. 创建SurfaceView | --> | 2. 初始化EGL环境   | --> | 3. 渲染线程管理    |
+--------------------+     +--------------------+     +--------------------+

1. 创建SurfaceView

继承SurfaceView并实现SurfaceHolder.Callback接口,管理Surface生命周期:

kotlin 复制代码
class MySurfaceView(context: Context, attrs: AttributeSet) : SurfaceView(context, attrs), SurfaceHolder.Callback {

    init {
        holder.addCallback(this)
    }

    private var mEGLHelper = MyEGLHelper()
    private var mEGLRender = MyEGLRender(context)
    private var mEGLThread: MyEGLThread? = null

    override fun surfaceCreated(holder: SurfaceHolder) {
        // 创建并启动渲染线程
        mEGLThread = MyEGLThread(holder.surface).apply {
            start()
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
        mEGLThread?.changeSize(width, height)
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        mEGLThread?.release()
    }
	
	// 渲染线程实现
	inner class MyEGLThread(private val mSurface: Surface) : Thread() {
        private var mWidth = 0
        private var mHeight = 0

        @Volatile
        private var mRunning = true

        @Volatile
        private var mSizeChanged = false
        override fun run() {
            super.run()
            try {
                mEGLHelper.initEGL(mSurface)
                mEGLRender.onSurfaceCreated()
                while (mRunning) {
                    // 宽高变化,回调渲染器的onSurfaceChanged方法
                    if (mSizeChanged) {
                        mEGLRender.onSurfaceChanged(mWidth, mHeight)
                        mSizeChanged = false
                    }
                    // 渲染一帧, 回调渲染器的onDrawFrame方法
                    mEGLRender.onDrawFrame()
                    mEGLHelper.swapBuffer()
                }
            } catch (e: Exception) {
                Log.e("yang", "EGL thread error ${e.message}")
            }
        }

        fun changeSize(width: Int, height: Int) {
            mWidth = width
            mHeight = height
            mSizeChanged = true
        }

        fun release() {
            mRunning = false
            mEGLRender.onSurfaceDestroyed()
            mEGLHelper.releaseEGL()
            interrupt()
        }
    }

2. 初始化EGL环境

负责EGL环境的初始化、配置和销毁:

kotlin 复制代码
class MyEGLHelper {
    private lateinit var mEGL: EGL10
    private lateinit var mEGLDisplay: EGLDisplay
    private lateinit var mEGLContext: EGLContext
    private lateinit var mEGLSurface: EGLSurface

    // 初始化EGL
    fun initEGL(surface: Surface) {
        if (::mEGL.isInitialized &&
            ::mEGLDisplay.isInitialized &&
            ::mEGLContext.isInitialized &&
            ::mEGLSurface.isInitialized) {
            Log.e("yang", "EGL already initialized")
            return
        }
        // 1. 获取EGL实例
        mEGL = EGLContext.getEGL() as EGL10

        // 2. 获取默认的显示设备(就是窗口)
        mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
        takeIf { mEGLDisplay == EGL10.EGL_NO_DISPLAY }?.apply {
            throw RuntimeException("eglGetDisplay failed")
        }

        // 3. 初始化默认显示设备
        val version = IntArray(2)
        takeIf { !mEGL.eglInitialize(mEGLDisplay, version) }?.apply {
            throw RuntimeException("eglInitialize failed")
        }

        // 4. 设置显示设备的属性
        val display_attribute_list = intArrayOf(
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_ALPHA_SIZE, 8,
            EGL_DEPTH_SIZE, 8,
            EGL_STENCIL_SIZE, 4,
            EGL_NONE
        )

        // 5. 查找配置并进行 attribute_list 的匹配, 匹配成功返回一个数组
        val num_config = IntArray(1)
        takeIf {
            !mEGL.eglChooseConfig(
                mEGLDisplay,
                display_attribute_list,
                null,
                0,
                num_config
            )
        }?.apply {
            throw RuntimeException("eglChooseConfig failed")
        }
        // 匹配是否成功
        takeIf { num_config[0] <= 0 }?.apply {
            throw RuntimeException("eglChooseConfig#1 failed")
        }
        val eglConfigs = arrayOfNulls<EGLConfig>(num_config[0])

        takeIf {
            !mEGL.eglChooseConfig(
                mEGLDisplay,
                display_attribute_list,
                eglConfigs,
                num_config[0],
                num_config
            )
        }?.apply {
            throw RuntimeException("eglChooseConfig#2 failed")
        }

        // 6. 创建EGLContext
        val context_display_list = intArrayOf(
            EGL_CONTEXT_CLIENT_VERSION, 3,
            EGL_NONE
        )
        takeIf { ::mEGLContext.isInitialized == false }?.apply {
            mEGLContext = mEGL.eglCreateContext(
                mEGLDisplay,
                eglConfigs[0],
                EGL10.EGL_NO_CONTEXT,
                context_display_list
            )
        }
        takeIf { mEGLContext == EGL10.EGL_NO_CONTEXT }?.apply {
            throw RuntimeException("eglCreateContext failed")
        }

        // 7. 创建EGLSurface
        takeIf { ::mEGLSurface.isInitialized == false }?.apply {
            mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, eglConfigs[0], surface, null)
        }
        takeIf { mEGLSurface == EGL10.EGL_NO_SURFACE }?.apply {
            throw RuntimeException("eglCreateWindowSurface failed")
        }

        // 8. 绑定EGLContext和EGLSurface
        takeIf { !mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext) }?.apply {
            throw RuntimeException("eglMakeCurrent failed")
        }
    }

    // 释放EGL
    fun releaseEGL() {
        takeIf { ::mEGL.isInitialized }?.apply {
            // 解绑EGLContext和EGLSurface
            mEGL.eglMakeCurrent(
                mEGLDisplay,
                EGL10.EGL_NO_SURFACE,
                EGL10.EGL_NO_SURFACE,
                EGL10.EGL_NO_CONTEXT
            )
            // 释放EGLSurface
            mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)
            // 释放EGLContext
            mEGL.eglDestroyContext(mEGLDisplay, mEGLContext)
            // 释放EGLDisplay
            mEGL.eglTerminate(mEGLDisplay)
        }
    }

    // 交换渲染数据
    fun swapBuffer() {
        takeIf { ::mEGL.isInitialized && ::mEGLDisplay.isInitialized }?.apply {
            takeIf { !mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface) }?.apply {
                throw RuntimeException("eglSwapBuffers failed")
            }
        }
    }
}

3. 渲染线程管理

在独立线程中处理渲染循环:

kotlin 复制代码
inner class MyEGLThread(private val mSurface: Surface) : Thread() {
        private var mWidth = 0
        private var mHeight = 0

        @Volatile
        private var mRunning = true

        @Volatile
        private var mSizeChanged = false
        override fun run() {
            super.run()
            try {
                mEGLHelper.initEGL(mSurface)
                mEGLRender.onSurfaceCreated()
                while (mRunning) {
                    // 宽高变化,回调渲染器的onSurfaceChanged方法
                    if (mSizeChanged) {
                        mEGLRender.onSurfaceChanged(mWidth, mHeight)
                        mSizeChanged = false
                    }
                    // 渲染一帧, 回调渲染器的onDrawFrame方法
                    mEGLRender.onDrawFrame()
                    mEGLHelper.swapBuffer()
                }
            } catch (e: Exception) {
                Log.e("yang", "EGL thread error ${e.message}")
            }
        }

        fun changeSize(width: Int, height: Int) {
            mWidth = width
            mHeight = height
            mSizeChanged = true
        }

        fun release() {
            mRunning = false
            mEGLRender.onSurfaceDestroyed()
            mEGLHelper.releaseEGL()
            interrupt()
        }
    }

4. 渲染器接口

定义渲染器接口,类似于GLSurfaceView.Renderer

kotlin 复制代码
interface EGLRender {
    fun onSurfaceCreated()  // Surface创建时调用
    fun onSurfaceChanged(width: Int, height: Int)  // Surface尺寸变化时调用
    fun onDrawFrame()  // 每帧渲染时调用
    fun onSurfaceDestroyed()  // Surface销毁时调用
}

5. 渲染器实现

实现渲染器接口,处理具体的OpenGL渲染逻辑:

kotlin 复制代码
class MyEGLRender(private val mContext: Context) : EGLRender {

    override fun onSurfaceCreated() {
        GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
        // 加载纹理...
        // 初始化顶点缓冲区...
        // 初始化着色器程序...
    }

    override fun onSurfaceChanged(width: Int, height: Int) {
        GLES30.glViewport(0, 0, width, height)
        // 改变渲染数据的宽高...
    }

    override fun onDrawFrame() {
        GLES30.glEnable(GLES30.GL_DEPTH_TEST)
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)
        // 具体绘制方法...
    }

    override fun onSurfaceDestroyed() {
        // 销毁渲染数据...
    }
}

完整流程图

整体渲染流程图

kotlin 复制代码
+----------------------+     +----------------------+
| SurfaceView创建      | --> | 注册SurfaceHolder    |
| MySurfaceView构造函数|     | Callback回调        |
+----------------------+     +----------------------+
           |
           v
+----------------------+     +----------------------+     +----------------------+
| Surface创建          | --> | 创建渲染线程        | --> | 初始化EGL环境        |
| surfaceCreated回调   |     | MyEGLThread.start() |     | mEGLHelper.initEGL() |
+----------------------+     +----------------------+     +----------------------+
           |                                                        |
           |                                                        v
+----------------------+     +----------------------+     +----------------------+
| Surface尺寸变化      | --> | 通知渲染线程        |     | 初始化渲染器         |
| surfaceChanged回调   |     | changeSize()        |     | onSurfaceCreated()   |
+----------------------+     +----------------------+     +----------------------+
           |                                                        |
           |                                                        v
+----------------------+                                 +----------------------+
| Surface销毁          |                                 | 渲染循环开始         |
| surfaceDestroyed回调 |                                 | 循环检查尺寸变化     |
+----------------------+                                 +----------------------+
           |                                                        |
           v                                                        v
+----------------------+     +----------------------+     +----------------------+
| 停止渲染线程        | <-- | 释放OpenGL资源       | <-- | 执行渲染操作        |
| mEGLThread.release() |     | onSurfaceDestroyed() |     | onDrawFrame()       |
+----------------------+     +----------------------+     +----------------------+
           |                                                        |
           v                                                        v
+----------------------+     +----------------------+     +----------------------+
| 释放EGL资源         |     | 线程退出            |     | 交换缓冲区          |
| mEGLHelper.releaseEGL()|     | 渲染循环结束        |     | swapBuffer()        |
+----------------------+     +----------------------+     +----------------------+

EGL初始化流程图

kotlin 复制代码
+------------------+     +------------------+     +------------------+
| 获取EGL实例      | --> | 获取显示设备     | --> | 初始化显示设备   |
| mEGL = EGLContext|     | eglGetDisplay    |     | eglInitialize    |
| .getEGL()        |     |                  |     |                  |
+------------------+     +------------------+     +------------------+
         |                                                 |
         v                                                 v
+------------------+     +------------------+     +------------------+
| 设置EGL属性      | --> | 选择EGL配置      | --> | 创建EGL上下文    |
| RGB/Alpha/Depth  |     | eglChooseConfig  |     | eglCreateContext |
+------------------+     +------------------+     +------------------+
         |                                                 |
         v                                                 v
+------------------+     +------------------+
| 创建渲染Surface  | --> | 绑定EGL组件      |
| eglCreateWindow  |     | eglMakeCurrent   |
| Surface          |     |                  |
+------------------+     +------------------+
渲染线程管理流程图
+------------------+     +------------------+     +------------------+
| 线程启动         | --> | 初始化EGL环境    | --> | 初始化渲染器     |
| Thread.start()   |     | initEGL(surface) |     | onSurfaceCreated |
+------------------+     +------------------+     +------------------+
         |                                                 |
         v                                                 v
+------------------+     +------------------+     +------------------+
| 渲染循环         | --> | 检查尺寸变化     | --> | 执行渲染         |
| while(mRunning)  |     | if(mSizeChanged) |     | onDrawFrame()    |
+------------------+     +------------------+     +------------------+
         |                          |                      |
         |                          v                      v
         |              +------------------+     +------------------+
         |              | 更新视口         |     | 交换缓冲区       |
         |              | onSurfaceChanged |     | swapBuffer()     |
         |              +------------------+     +------------------+
         |                                                 |
         v                                                 v
+------------------+     +------------------+     +------------------+
| 接收释放信号     | --> | 通知渲染器销毁   | --> | 释放EGL资源      |
| release()调用    |     | onSurfaceDestroy |     | releaseEGL()     |
+------------------+     +------------------+     +------------------+
         |
         v
+------------------+
| 线程结束         |
| interrupt()      |
+------------------+
相关推荐
Hxyle12 分钟前
c++设计模式
开发语言·c++·设计模式
blammmp33 分钟前
算法专题四:前缀和
java·开发语言·算法
www_pp_36 分钟前
# 创建一个功能完备的计算器应用:使用PyQt5和Python
开发语言·python·qt
Aimyon_361 小时前
Java复习笔记-基础
java·开发语言·笔记
卡尔曼的BD SLAMer1 小时前
问题 | 当前计算机视觉迫切解决的问题
图像处理·人工智能·深度学习·计算机视觉·信息与通信
明月看潮生1 小时前
青少年编程与数学 02-018 C++数据结构与算法 25课题、图像处理算法
c++·图像处理·算法·青少年编程·编程与数学
鸿蒙布道师1 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork1 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
codefly-xtl2 小时前
责任链设计模式
java·开发语言·设计模式
非晓为骁2 小时前
【Go】优化文件下载处理:从多级复制到零拷贝流式处理
开发语言·后端·性能优化·golang·零拷贝