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. 渲染器实现
实现渲染器接口,处理具体的OpenG
L渲染逻辑:
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() |
+------------------+