了解了,你的需求是:只有一个 GLSurfaceView(拥有一个渲染线程),但你需要开启另一个后台线程(子线程)并在该线程中操作 OpenGL 资源(如异步加载纹理),这两个线程需要共享同一个 Context。
这是 Android 高性能渲染(如异步上传纹理、后台滤镜处理)的经典场景。
核心逻辑
- 捕获主线程 Context:通过自定义
GLSurfaceView.EGLContextFactory拿到GLSurfaceView创建好的EGLContext。 - 后台线程初始化 EGL:在子线程中手动初始化 EGL 环境,并调用
eglCreateContext时,将主线程的EGLContext作为共享参数传入。 - 使用离屏 Surface:子线程通常不需要显示,所以使用
PbufferSurface(像素缓冲表面)。
代码实现
1. 自定义 ContextFactory (用于获取主线程 Context)
我们需要拿到 GLSurfaceView 内部创建的那个 EGLContext。
1 | public class MyContextFactory implements GLSurfaceView.EGLContextFactory { |
2. 后台线程的 EGL 环境 (使用 EGL14)
Android 现代开发推荐使用 android.opengl.EGL14。
1 | public class BackgroundGLThread extends Thread { |
3. Activity 中的整合
1 | public class MainActivity extends AppCompatActivity { |
关键点详解
EGL10 vs EGL14:
-
GLSurfaceView内部默认使用javax.microedition.khronos.egl(EGL10)。 - 现代子线程操作建议使用
android.opengl.EGL14。 - 转换技巧:在
onSurfaceCreated里直接调用EGL14.eglGetCurrentContext(),由于该方法是在渲染线程调用的,它能直接拿到当前激活的 EGL14 句柄,哪怕GLSurfaceView内部是用 EGL10 创建的。
-
glFinish()的必要性:- 在子线程完成资源创建(如
glTexImage2D)后,必须调用GLES20.glFinish()。这能确保 GPU 执行完了所有指令,主线程再去访问该纹理时数据才是完整的。
- 在子线程完成资源创建(如
PbufferSurface:
- OpenGL 必须绑定一个 Surface 才能工作。子线程没有 UI,所以用
eglCreatePbufferSurface创建一个 1x1 的离屏缓冲区即可。
- OpenGL 必须绑定一个 Surface 才能工作。子线程没有 UI,所以用
纹理 ID 的共享:
- 在子线程生成的
textureId(int 类型) 是可以在主线程直接使用的。你只需要通过 Handler 或线程通信手段把这个 int 值传回给 Renderer 即可。
- 在子线程生成的
适用场景
- 异步加载大纹理:避免
glTexImage2D阻塞 UI 渲染导致掉帧。 - 多线程并行处理:一个线程负责读取摄像头并做初步美颜(写入 FBO/Texture),主线程负责显示和 UI 覆盖。