本篇算是java层中关于OpenGL ES的最后一篇,前三篇文章我们具体分析了使用流程,这一篇我们将会分析EGL,EGL是贯穿我们OpenGL ES一个很重要的概念,理解它的作用有助于我们更好的理解OpenGL ES
老规矩我们先看下关于EGL在源码中使用到的地方:
private static class EglHelper {
public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
// 用给点的配置属性初始化EGL
public void start() {
// 获取EGL实例
mEgl = (EGL10) EGLContext.getEGL();
// 进入默认显示
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
// 为EglDisplay实例初始化EGL
int[] version = new int[2];
if(!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view == null) {
mEglConfig = null;
mEglContext = null;
} else {
mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
// 创建EGL上下文,不过要尽可能少的创建,因为EGL上下文对象很重
mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
}
// 没有上下文就要抛异常,当对应的view对象被回收就会执行
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
mEglContext = null;
throwEglException("createContext");
}
mEglSurface = null;
}
// 为当前的SurfaceHolder surface创建一个egl surface,如果已经存在就销毁重建
public boolean createSurface() {
// 使用这步,必须要保证egl对象,display和config都存在
if (mEgl == null) {
throw new RuntimeException("egl not initialized");
}
if (mEglDisplay == null) {
throw new RuntimeException("eglDisplay not initialized");
}
if (mEglConfig == null) {
throw new RuntimeException("mEglConfig not initialized");
}
// 如果窗口大小改变,我们就要新建一个surface
destroySurfaceImp();
// 创建一个我们能渲染的surface
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
mEglDisplay, mEglConfig, view.getHolder());
} else {
mEglSurface = null;
}
// 如果对应view被回收或者surface创建失败,就返回创建失败
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
...
return false;
}
// 在我们发出GL命令之前,我们需要确认上下文是当前上下文,并且
// 被绑定到当前的surface
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
return false;
}
return true;
}
// 给当前EGL上下文创建一个GL对象
GL createGL() {
GL gl = mEglContext.getGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
if (view.mGLWrapper != null) {
gl = view.mGLWrapper.wrap(gl);
}
...
}
return gl;
}
// 展示当前渲染的表面,这里很好的说明了上一篇文章的一个关键点,就是
// 真正展示出来,并不是在onDrawFrame中
public int swap() {
if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
return mEgl.eglGetError();
}
return EGL10.EGL_SUCCESS;
}
...
private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef;
EGL10 mEgl;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
}
其实这个类挺长的,我只是留了些比较关键的步骤,但是即使到了这里,我们对EGL还是很懵逼,只是知道EGL Context、EGL Surface和EGL Display是很重要,EGL Context提供了上下文环境,最后的渲染最后都会被渲染到EGL Surface,而最终的的显示是通过EGL Display来完成的。
1. 什么是EGL?
EGL:Embedded Graphics Library,是连接OpenGL ES和本地窗口系统的接口,由于OpenGL ES是跨平台的,引入EGL就是为了屏蔽不同平台上的区别。
2. EGL的执行顺序是什么?
- 建立本地窗口系统和
OpenGL ES的连接,拿到EGLDisplay- 通过
EGLDisplay初始化EGL,使用eglInitialize函数- 确定可用的渲染表面
surface的配置,一旦初始化了EGL,就可以确定可用渲染表面的类型和配置了,有两种方法,一是通过eglGetConfigs、eglGetConfigAttrib,另一种就是通过eglChooseChofig- 创建渲染表面,有了上一步获取的
EGLConfig后,就可以调用eglCreateWindowSurface来创建渲染表面。- 创建渲染上下文,使用
eglCreateContext创建EGL渲染上下文,然后调用eglBindAPI来将此上下文绑定到当前API- 指定某个
EGL Context为当前上下文,使用eglMakeCurrent函数进行当前上下文的绑定,因为一个程序可能创建多个EGL Context- 使用
OpenGL相关的API进行绘制操作- 交换
EGL的Surface的内部缓冲和EGL创建的和平台无关的窗口diaplay,即调用eglSwapBuffers(mEglDisplay, mEglSurface),如果surface是一个window surface,那么该函数执行的结果将是将数据给本地窗口,即显示在屏幕上。如果surface是一个屏幕外渲染surface(pixel buffer),执行该函数没有效果。
具体的描述,可以参考EGL介绍这篇文章。
总结:
通过上面的EGL执行过程分析和源码分析,再结合之前的三篇文章,我相信对于OpenGL ES的执行过程,我们已经有了一个比较清晰的认识。