OpenGL ES 之EGL(6)
简述
EGL是OpenGL ES的封装,目的是跨设备跨平台,隔离不同平台对窗口不同的实现。上一节我们基本没有使用到EGL,因为GLSurfaceView帮助我们处理了相关的逻辑,我们这一节来看一下EGL的一些概念以及接口的使用。
同时我们会介绍GLSurfaceView做了什么,是怎么配置EGL等。
EGL接口
- 1.eglGetDisplay
用于获取EGLDisplay,这里会关联原生窗口,EGLDisplay是对设备的抽象。 - 2.eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion)
初始化函数,第一个参数是eglGetDisplay返回值。 - 3.eglChooseConfig
EGL会根据设备配置选择合适的Config - 4.eglCreateWindowSurface
通过前面EGLDisplay和EGLConfig创建EGLSurface - 5.eglCreateContext
创建EGLContext,创建渲染上下文 - 6.eglMakeCurrent
绑定EGLContext,EGLSurface,EGLDisplay,之后即可调用openGL ES的api做图像渲染了。 - 7.eglSwapBuffers
交换缓冲区,调用后就会将内存中的图像显示到屏幕上。
GLSurfaceView流程
setRenderer
配置了Renderer之后,GLSurfaceView启动了一个GLThread线程
public void setRenderer(Renderer renderer) {
checkRenderThreadState();
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
// 构造并启动了一个GLThread线程
mGLThread = new GLThread(renderer);
mGLThread.start();
}
GLThread
调用了guardedRun。
guardedRun通过一个EglHelper来调用EGL的接口。
guardedRun在一个死循环中,死循环中还有一个死循环,这里会通过mEglHelper.start来初始化EGL。在EGLSurface创建好后,就会跳出这个死循环,在外层循环后面的逻辑,首次会通过createSurface创建EGLSurface,并且回调Renderer.onSurfaceCreated,也会检查sizeChanged,如果sizeChanged则会回调Renderer.onSurfaceChanged。
每次循环都会回调Renderer.onDrawFrame,在回调onDrawFrame之后会调用mEglHelper.swap来执行交换区。
这里EglHelper的start/createSurface/swap,我们接下来看看这几个方法。
private class GLThread extends Thread {
// ...
public void run() {
setName("GLThread " + getId());
if (LOG_THREADS) {
DebugLog.i("GLThread", "starting tid=" + getId());
}
try {
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
sGLThreadManager.threadExiting(this);
}
}
}
private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper();
// ...
try {
// ...
while (true) {
synchronized (sGLThreadManager) {
while (true) {
// ...
if ((! mHasSurface) && (! mWaitingForSurface)) {
if (LOG_SURFACE) {
DebugLog.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
}
if (mHaveEglSurface) {
stopEglLocked();
}
mWaitingForSurface = true;
sGLThreadManager.notifyAll();
}
// ...
// Ready to draw?
if ((!mPaused) && mHasSurface
&& (mWidth > 0) && (mHeight > 0)
&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
if (mHaveEglContext && !mHaveEglSurface) {
// 检测EGL上下文
if (!mEglHelper.verifyContext()) {
mEglHelper.finish();
mRenderer.onSurfaceLost();
mHaveEglContext = false;
}
}
if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
mHaveEglContext = true;
// 启动EGLHelper.start,这里会做EGL的初始化
mEglHelper.start();
sGLThreadManager.notifyAll();
}
// ...
if (mHaveEglSurface) {
// ... 配置宽高
break;
}
}
sGLThreadManager.wait();
}
} // end of synchronized(sGLThreadManager)
if (event != null) {
event.run();
event = null;
continue;
}
if (mHasFocus) {
if (createEglSurface) {
// 调用createSurface,初始化EGL上下文
gl = (GL10) mEglHelper.createSurface(getHolder());
// ...
// 回调Renderer.onSurfaceCreated
mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
createEglSurface = false;
framesSinceResetHack = 0;
}
if (sizeChanged) {
// ...
// 回调Renderer.onSurfaceChanged
mRenderer.onSurfaceChanged(gl, w, h);
sizeChanged = false;
}
// ...
mWatchDog.reset();
// 回调Renderer.onDrawFrame
mRenderer.onDrawFrame(gl);
framesSinceResetHack++;
// 调用eglSwapBuffers,交换缓冲区上屏显示
if(!mEglHelper.swap()) {
// ...
stopEglLocked();
}
}
if (wantRenderNotification) {
doRenderNotification = true;
}
}
} finally {
// ... 释放EGL上下文
}
}
EglHelper
EglHelper就是对EGL对接口进行封装,这些EGL的接口作用在前面都介绍过了。
public void start(){
mEgl = (EGL10) EGLContext.getEGL();
// 通过eglGetDisplay获取EglDisplay
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// ...
int[] version = new int[2];
// 调用eglInitialize进行初始化
if(!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
// 调用eglChooseConfig获取EglConfig
mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
// 创建EglContext
mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
throwEglException("createContext");
}
mEglSurface = null;
}
public GL createSurface(SurfaceHolder holder) {
// 如果之前创建过EglSurface,直接调用eglMakeCurrent进行关联
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
}
// 调用createWindowSurface创建EglSurface
mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
mEglDisplay, mEglConfig, holder);
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
throwEglException("createWindowSurface");
}
// 调用eglMakeCurrent关联
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throwEglException("eglMakeCurrent");
}
GL gl = mEglContext.getGL();
if (mGLWrapper != null) {
gl = mGLWrapper.wrap(gl);
}
// ... 配置debug相关flag
return gl;
}
public boolean swap() {
// 调用eglSwapBuffers交换Buffer显示
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
}
小结
EGL的接口比较简单,流程也基本是固定的,我们以GLSurfaceView为例介绍了它的使用流程,GLSurfaceView就是启动一个线程,除了处理固定的EGL上下文初始化,还控制了Renderer回调的几个生命周期。
介绍完EGL后,我们后面就可以专注于OpenGL ES的api使用了。