综述:
在opengl的使用场景中我们经常遇到需要使用opengl的环境进行数据处理,比如在对Opengl进行视频帧数据处理之后需要继续对视频数据编码,比如在一些场景我们需要对视频帧处理,但是并不需要进行上屏显示等处理,比如我们在对一些视频在播放时需要再解码完成之后对视频进行特效处理等。此三种场景无论我们是否已经有了opengl的环境,都需要我们在进行业务时,基于opengl的环境,也就是Egl环境下才能进行处理,有些场景我们能借助当前的opengl环境进行处理,我们需要创建EGL环境的时候 共享当前的opengl环境,即将当前egl的context传递出去,有些场景没有opengl的环境,需要我们自己单独创建EGL环境。EGL环境时使用opengl必须创建的,在Android 中我们使用GlSurfaceView时,就是系统封装好了EGL环境,因此我们在自己使用EGL环境的时候,可以仿照系统进行创建,然后进行管理
1:EGL是什么
EGL是什么,简单来说,opengl是一个跨平台的图形绘制API,opengl可以在不同的操作系统上实现图形绘制,使用同一套API,但是针对不同的平台之间的差异带来的绘制差异如何解决?这个时候由相关组织就抽象了一层,就是EGL,在不同的平台上实现opengl环境绘制之前,都需要实现EGL环境,根据不同平台配置好EGL环境之后,我们使用opengl来绘制就可以跨平台了
上面这张经典的图可以说明 EGL在使用opengl绘制时所处的位置,在Android中使用opengl,由系统实现了一套 EGL的环境,可以参考GlSurfaceView的源码
2:EGL怎么用
EGL怎么使用?请参考下图:
EGL总共管理的3个大类型的抽象类,一个是 EGLDisplay ,一个是EGLContext,还有一个是EGLSurface,EglContext还可以提供给到其他线程,则不同的线程之间可以共享EGL的环境,下面介绍下一般的创建过程
public BaseEglCore(EGLContext sharedContext, int flags) {
throwableForStackTrace = new Throwable();
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("EGL already set up");
}
if (sharedContext == null) {
sharedContext = EGL14.EGL_NO_CONTEXT;
}
//创建EGLDisplay
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
}
//初始化EGLDisplay
int[] version = new int[2];
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
mEGLDisplay = null;
throw new RuntimeException("unable to initialize EGL14");
}
// Try to get a GLES3 context, if requested.
if ((flags & FLAG_TRY_GLES3) != 0) {
//Log.d(TAG, "Trying GLES 3");
//配置opengl的环境
EGLConfig config = getConfig(flags, 3);
if (config != null) {
int[] attrib3List = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
EGL14.EGL_NONE
};
//创建EGLcontext
EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
attrib3List, 0);
if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
//Log.d(TAG, "Got GLES 3 config");
mEGLConfig = config;
mEGLContext = context;
mGlVersion = 3;
}
}
}
// Confirm with query.
int[] values = new int[1];
EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
values, 0);
Log.d(TAG, "EGLContext created, client version " + values[0]);
}
private EGLConfig getConfig(int flags, int version) {
int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
if (version >= 3) {
renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
}
// The actual surface is generally RGBA or RGBX, so situationally omitting alpha
// doesn't really help. It can also lead to a huge performance hit on glReadPixels()
// when reading into a GL_RGBA buffer.
int[] attribList = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
//EGL14.EGL_DEPTH_SIZE, 16,
//EGL14.EGL_STENCIL_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, renderableType,
EGL14.EGL_NONE, 0, // placeholder for recordable [@-3]
EGL14.EGL_NONE
};
if ((flags & FLAG_RECORDABLE) != 0) {
attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
attribList[attribList.length - 2] = 1;
}
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
numConfigs, 0)) {
Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
return null;
}
return configs[0];
}
创建好了 EglCore之后,我们就已经创建成功了 EglDisplay,EglContext,这个时候我们的普通线程已经成为了GLthread线程,但是我们的EGL环境尚未创建完成,正如前面所说,EGL需要的三个抽象实体还未全部创建出来,想要在当前的线程中使用opengl API,还需要创建EglSurface,创建过程如下:
//创建一个离屏的egl Surface
public OffscreenSurface(BaseEglCore eglCore, int width, int height) {
super(eglCore);
createOffscreenSurface(width, height);
}
public void createOffscreenSurface(int width, int height) {
if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
throw new IllegalStateException("surface already created");
}
mEGLSurface = mEglCore.createOffscreenSurface(width, height);
mWidth = width;
mHeight = height;
}
/**
* Associates an EGL surface with the native window surface.
*
* Set releaseSurface to true if you want the Surface to be released when release() is
* called. This is convenient, but can interfere with framework classes that expect to
* manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the
* surfaceDestroyed() callback won't fire).
*/
//创建一个基于屏幕上egl Surface的
public WindowSurface(BaseEglCore eglCore, Surface surface, boolean releaseSurface) {
super(eglCore);
createWindowSurface(surface);
mSurface = surface;
mReleaseSurface = releaseSurface;
}
/**
* Creates a window surface.
*
* @param surface May be a Surface or SurfaceTexture.
*/
public void createWindowSurface(Object surface) {
if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
throw new IllegalStateException("surface already created");
}
mEGLSurface = mEglCore.createWindowSurface(surface);
// Don't cache width/height here, because the size of the underlying surface can change
// out from under us (see e.g. HardwareScalerActivity).
//mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
//mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
}
到此我们就把EGL的一个简单环境搭建起来了
3:EGL中常见使用场景
Egl在Android或者其他平台上,是使用Opengl的前置条件,需要自行配置。Android中一般如果要是在在屏幕上最终要显示出来图像可以使用Android中配置好的GlSurfaceView这个API,这个API已经由Android系统进行配置了一个EGL环境,使用者可以直接设置Render之后,在onDraw中进行绘制。
在一些场景中,我们可能不需要将图像最终显示在屏幕上,但是需要使用Opengl来对图像进行处理,典型几个应用场景如下:
3.1:在使用opengl进行多图层处理时,我们到最终显示前需要多次对纹理进行处理,此时如果我们没有opengl的环境,需要自行搭建,此种业务为我们需要对图像数据进行AI处理,需要将纹理作为参数喂给我们的AI的SDK,或者我们需要使用opengl对图像进行旋转,缩放,裁剪,多图层合并等2种常见业务场景。
3.2:在使用oepngl进行图像渲染上屏渲染时,我们在上了屏幕显示的同时需要对显示的数据进行录制,此时如何将之前在纹理上的数据进行录制?需要我们共享屏幕上显示的opengl环境的Eglcontext 到MediaCodec,让MediaCodec录制的时候采集的数据是纹理上的数据。主要常见的业务场景是推流等
3.3:在显示数据到屏幕上时,我们显示的数据是从视频文件中获取的,需要我们自己搭建Egl环境,即搭建Egl环境创建纹理穿件Surface给到MediaCodec解码,MediaCodec解码出来的数据直接给到刚才创建的OES纹理上,进行绘制。常见的使用场景播放器等
综上,使用Egl环境常见的几个场景,即如果不上屏幕显示,需要自行搭建EGL环境。其次是如果涉及到编码和解码,都需要自行创建EGL的环境或者共享已有的EGL环境来实现业务场景