前言
众所周知,EGL和Open GL ES是个概念,前者驱动绘制环境的运行和硬件展示,后者主要负责渲染。两者并非相互依赖。对于Android平台,每次使用open gl绘制,往往需要调用egl,但是没有方法上的互相调用,仅仅顺序上前者先调用,后者后调用,那么,这种调用关系是如何实现的?
做音视频开发经常要遇到分屏和跨页面切换Surface的需求,一般实现这类需求可以选用的方式包括Egl + open gl es、ImageReader、虚拟屏等,当然如果你的设备具备root权限还可以使用SurfaceControl。其中使用Egl的好处是可以在渲染前修改纹理数据,但是Egl和open gl es的纹理之间的关系显得莫名其妙,可能是egl和 open gl es不支持面向对象编程的缘故,在使用open gl es 生成纹理时,看不到两者存在任何关联关系。
ExoPlayer 内部案例
代码应用自com.google.android.exoplayer2.util.EGLSurfaceTexture
public void init(@SecureMode int secureMode) throws GlUtil.GlException {
display = getDefaultDisplay();
EGLConfig config = chooseEGLConfig(display);
context = createEGLContext(display, config, secureMode);
surface = createEGLSurface(display, config, context, secureMode);
generateTextureIds(textureIdHolder);
texture = new SurfaceTexture(textureIdHolder[0]);
texture.setOnFrameAvailableListener(this);
}
private static void generateTextureIds(int[] textureIdHolder) throws GlUtil.GlException{
GLES20.glGenTextures(/* n= */ 1, textureIdHolder, /* offset= */ 0);
GlUtil.checkGlError();
}
从上述代码我们可以很清楚的看到,生成EGLSurface之后,再生成的open gl纹理时 textName和egl没有产生任何引用关系。
按照标准的引用流程,应该是按照下面的方式
但是在这里我们并没有发现EGL和Open Gl具体引用关系,而textureId的生成调用的是open gl 的相关方法,似乎这里出现了断层。
关于Egl Context
在查阅了一些博客之后发现,其实Egl Context 是线程级别的,类似我们使用<Thread map List<Egl Context>>机制进行保存,在一个线程中,egl context 是可以存在多份的,但是同时只能使用其中一个Context。还有一点是多个线程不能共享同一个egl Context.
Egl Context 是线程级别的
Egl Context 在同一个线程可以有多个,但只能同时使用一个Context
Egl Context 无法实现跨线程贡献
egl与open gl es 密不可分
到这里我们应该知道另一个情况,很多人在Android平台上学习open gl es的时候总是忽视egl的存在,主要原因是开发过程中使用GLSurfaceView,因为GLSurfaceView使用屏蔽了对egl的调用,导致开发open gl时对egl的调用被忽视。实际使用open gl的时候,都需要使用egl去创建一个open gl开发环境,这个环境的状态保存在egl context中。egl context主要负责对opengl 、纹理、shader等管理。
可想而知,egl context 本质上和open gl的调用是有关联的,至于在方法调用层没有进行关联,可能是因为open gl面向流程的原因,在底层使用open gl很多情况下肯定需要访问egl context,而且这种访问保证了open gl的单线程模型。当然,在vulkan逐步覆盖的情况下,这种看起来莫名其妙的问题应该会少很多。
总结
open gl es 绘制其实是需要依赖egl context,在Android系统中,我们常常由于GLSurfaceView的封装而忽略这层关系,导致数据交换的时候往往很不理解。
通过本篇,我们对此有了深入的认识。