SurfaceView
+ 自定义EGL
实现OpenGL
渲染
在Android
开发中,当需要灵活控制OpenGL
渲染或在多个Surface
间共享EGL
上下文时,自定义EGL
环境是必要的选择
核心实现流程
+--------------------+ +--------------------+ +--------------------+
| 1. 创建SurfaceView | --> | 2. 初始化EGL环境 | --> | 3. 渲染线程管理 |
+--------------------+ +--------------------+ +--------------------+
1. 创建SurfaceView
继承SurfaceView
并实现SurfaceHolder.Callback
接口,管理Surface
生命周期:
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? = nulloverride 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 = 0private var mHeight = 0@Volatileprivate var mRunning = true@Volatileprivate var mSizeChanged = falseoverride 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 = widthmHeight = heightmSizeChanged = true}fun release() {mRunning = falsemEGLRender.onSurfaceDestroyed()mEGLHelper.releaseEGL()interrupt()}}
2. 初始化EGL
环境
负责EGL
环境的初始化、配置和销毁:
class MyEGLHelper {private lateinit var mEGL: EGL10private lateinit var mEGLDisplay: EGLDisplayprivate lateinit var mEGLContext: EGLContextprivate lateinit var mEGLSurface: EGLSurface// 初始化EGLfun 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. 创建EGLContextval 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. 创建EGLSurfacetakeIf { ::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和EGLSurfacetakeIf { !mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext) }?.apply {throw RuntimeException("eglMakeCurrent failed")}}// 释放EGLfun releaseEGL() {takeIf { ::mEGL.isInitialized }?.apply {// 解绑EGLContext和EGLSurfacemEGL.eglMakeCurrent(mEGLDisplay,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_CONTEXT)// 释放EGLSurfacemEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)// 释放EGLContextmEGL.eglDestroyContext(mEGLDisplay, mEGLContext)// 释放EGLDisplaymEGL.eglTerminate(mEGLDisplay)}}// 交换渲染数据fun swapBuffer() {takeIf { ::mEGL.isInitialized && ::mEGLDisplay.isInitialized }?.apply {takeIf { !mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface) }?.apply {throw RuntimeException("eglSwapBuffers failed")}}}
}
3. 渲染线程管理
在独立线程中处理渲染循环:
inner class MyEGLThread(private val mSurface: Surface) : Thread() {private var mWidth = 0private var mHeight = 0@Volatileprivate var mRunning = true@Volatileprivate var mSizeChanged = falseoverride 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 = widthmHeight = heightmSizeChanged = true}fun release() {mRunning = falsemEGLRender.onSurfaceDestroyed()mEGLHelper.releaseEGL()interrupt()}}
4. 渲染器接口
定义渲染器接口,类似于GLSurfaceView.Renderer
:
interface EGLRender {fun onSurfaceCreated() // Surface创建时调用fun onSurfaceChanged(width: Int, height: Int) // Surface尺寸变化时调用fun onDrawFrame() // 每帧渲染时调用fun onSurfaceDestroyed() // Surface销毁时调用
}
5. 渲染器实现
实现渲染器接口,处理具体的OpenG
L渲染逻辑:
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() {// 销毁渲染数据...}
}
完整流程图
整体渲染流程图
+----------------------+ +----------------------+
| 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
初始化流程图
+------------------+ +------------------+ +------------------+
| 获取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() |
+------------------+