GLSurfaceView的简单分析及巧妙借用
学习在Android中使用OpenGLES,就不得不提到一个控件:GLSurfaceView。本篇博客将介绍GLSurfaceView的基本使用、GLSurfaceView的源码的简单分析,以及使用GLSurfaceView渲染图像到SurfaceView/TextureView或者PBuffer上等。
关于GLSurfaceView
GLSurfaceView继承了SurfaceView,实现了SurfaceHolder.Callback2接口。
SurfaceView在View的基础上是创建了独立的Surface,拥有SurfaceHolder来管理它的Surface,渲染的工作可以不再在主线程中做了。可以通过SurfaceHolder得到Canvas,在单独的线程中,利用Canvas绘制需要显示的内容,然后更新到Surface上。
而GLSurfaceView,它主要是在SurfaceView的基础上实现了一个GLThread(EGLContext创建GL环境所在线程即为GL线程),绘制的工作直接通过OpenGL来进行,绘制的内容默认情况下依旧是绘制到SurfaceView所提供的Surface上。
参照GLSurfaceView的实现,我们可以自行创建GL环境,来进行GL渲染。实现自行制定指定载体、后台渲染等功能。
GLSurfaceView的使用
GLSurfaceView作为一个View,实例化上基本和其他View相当。可以选择在xml布局文件中增加,然后在Java代码中取得它的控制权。也可以在Java代码中直接new,然后呈现出来。GLSurfaceView必须加入到一个布局中,才能正确的使用,否则有可能会造成崩溃
,这和GLSurfaceView的attachToWindow和detachFromWindow中相关操作。
GLSurfaceView具有onResume和onPause两个同Activity及Fragment中的生命周期同名的方法。一般来说,在Activity或者Fragment中的onResume和onPause方法中,需要主动调用GLSurfaceView的实例的这两个方法。
GLSurfaceView的基本使用代码如下:
mGLView= (GLSurfaceView) findViewById(R.id.mGLView);//GLContext设置为OpenGLES2.0mGLView.setEGLContextClientVersion(2);//在setRenderer之前,可以调用以下方法来进行EGL设置//mGLView.setEGLConfigChooser(); //颜色、深度、模板等等设置//mGLView.setEGLWindowSurfaceFactory(); //窗口设置//mGLView.setEGLContextFactory(); //EGLContext设置//设置渲染器,渲染主要就是由渲染器来决定mGLView.setRenderer(new GLSurfaceView.Renderer(){ @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { //todo surface被创建后需要做的处理 } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { //todo 渲染窗口大小发生改变的处理 } @Override public void onDrawFrame(GL10 gl) { //todo 执行渲染工作 }});/*渲染方式,RENDERMODE_WHEN_DIRTY表示被动渲染,只有在调用requestRender或者onResume等方法时才会进行渲染。RENDERMODE_CONTINUOUSLY表示持续渲染*/mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
GLSurfaceView源码分析
GLSurfaceView中,对于GL环境的操作,出queueEvent是将事件放入队列中,到GL线程中执行外,其他方法基本都是在主线程(也可以是其他线程,非当前GLSurfaceView实例的GL线程)中修改某个状态值,然后取消GL线程的等待,在GL线程中根据状态值作相应的操作,并在操作后反馈给调用方法的那个线程,当然有的方法也不需要反馈。
GLSurfaceView主要方法及中文注释如下:
public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 { public final static int RENDERMODE_WHEN_DIRTY = 0; public final static int RENDERMODE_CONTINUOUSLY = 1; public GLSurfaceView(Context context) { super(context); init(); } public GLSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); init(); } //调试用的 public void setDebugFlags(int debugFlags); public int getDebugFlags(); //设置暂停的时候是否保持EglContext public void setPreserveEGLContextOnPause(boolean preserveOnPause); public boolean getPreserveEGLContextOnPause(); //设置渲染器,这个非常重要,渲染工作就依靠渲染器了 //调用此方法会开启一个新的线程,即GL线程 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(); } mRenderer = renderer; mGLThread = new GLThread(mThisWeakRef); mGLThread.start(); } //设置EGLContext工厂,不设置就用默认的 public void setEGLContextFactory(EGLContextFactory factory); //设置EGLSurface工厂,不设置就用默认的 public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory); //设置EglConfig,一般颜色深度等等,利用此方法设置。不设置就用默认的 public void setEGLConfigChooser(EGLConfigChooser configChooser); //内部调用setEGLConfigChooser public void setEGLConfigChooser(boolean needDepth); //内部调用setEGLConfigChooser public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize); //设置EGLContextVersion,比如2,即OpenGLES2.0 public void setEGLContextClientVersion(int version); //设置渲染方式,有RENDERMODE_CONTINUOUSLY表示不断渲染 //以及RENDERMODE_WHEN_DIRTY表示在需要的时候才会渲染 //渲染的时候要求调用requestRender,必须在setRenderer后调用 public void setRenderMode(int renderMode); public int getRenderMode(); //主动请求渲染 public void requestRender(); public void surfaceCreated(SurfaceHolder holder); public void surfaceDestroyed(SurfaceHolder holder); public void surfaceChanged(SurfaceHolder holder, int format, int w, int h); @Override public void surfaceRedrawNeeded(SurfaceHolder holder) { if (mGLThread != null) { mGLThread.requestRenderAndWait(); } } //生命周期,一般在Activity、Fragment的onPause中调用 public void onPause(); //生命周期,一般在Activity、Fragment的onResume中调用 public void onResume(); //向GL线程发送一个任务 public void queueEvent(Runnable r); //附加到Window上时被调用,外部不可调用 protected void onAttachedToWindow(); //从Window上被移除时调用,外部不可调用 protected void onDetachedFromWindow(); //渲染器接口 public interface Renderer { //Surface被创建时被调用,通常在此进行渲染的初始化 void onSurfaceCreated(GL10 gl, EGLConfig config); //Surface大小被改变时被调用 void onSurfaceChanged(GL10 gl, int width, int height); //执行渲染时被调用,以完成用户渲染工作 void onDrawFrame(GL10 gl); } //非常重要的一个EGL帮助类,GL环境的建立依靠此类 private static class EglHelper { public EglHelper(WeakReference glSurfaceViewWeakRef) { mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; } //EGL的初始化,可以参考此方法 public void start() { if (LOG_EGL) { Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); } /* * Get an EGL instance */ mEgl = (EGL10) EGLContext.getEGL(); /* * Get to the default display. */ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { throw new RuntimeException("eglGetDisplay failed"); } /* * We can now initialize EGL for that display */ 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); /* * Create an EGL context. We want to do this as rarely as we can, because an * EGL context is a somewhat heavy object. */ mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); } if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { mEglContext = null; throwEglException("createContext"); } if (LOG_EGL) { Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId()); } mEglSurface = null; } //创建EGLSurface,使GL的渲染,能够渲染到用户指定的Surface //默认的Surface就是SurfaceHolder的Surface public boolean createSurface() { if (LOG_EGL) { Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); } /* * Check preconditions. */ 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"); } /* * The window size has changed, so we need to create a new * surface. */ destroySurfaceImp(); /* * Create an EGL surface we can render into. */ GLSurfaceView view = mGLSurfaceViewWeakRef.get(); if (view != null) { mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, view.getHolder()); } else { mEglSurface = null; } if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { int error = mEgl.eglGetError(); if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); } return false; } /* * Before we can issue GL commands, we need to make sure * the context is current and bound to a surface. */ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { /* * Could not make the context current, probably because the underlying * SurfaceView surface has been destroyed. */ logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); return false; } return true; } //通过EGL得到GL,然后用户设置了Wrapper的话会给得到的GL做个包装 //同时也会解析一下用户的Debug意图,看看要不要debug GL createGL(); //绘制完成之后,调用此方法,将绘制的内容输出到前台,让用户可以看到 public int swap(); //销毁Surface的方法,具体实现在destroySurfaceImp方法中 public void destroySurface(); //销毁GL环境 public void finish(); } //GL线程,此类中存在的方法,GLSurfaceView中有同名的, //基本都是提供给GLSurfaceView作为真正的实现调用 static class GLThread extends Thread { //销毁EglSurface private void stopEglSurfaceLocked(); //销毁EglContext private void stopEglContextLocked(); //GL线程的主要逻辑都在这个方法里面,这个方法比较复杂 //GLSurfaceView的核心就在这个里面了,最后在单独分析这个里面的逻辑 private void guardedRun() throws InterruptedException; public boolean ableToDraw() { return mHaveEglContext && mHaveEglSurface && readyToDraw(); } private boolean readyToDraw() { return (!mPaused) && mHasSurface && (!mSurfaceIsBad) && (mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); } //设置渲染方法,见GLSurfaceView的setRenderMode public void setRenderMode(int renderMode); public int getRenderMode(); //请求一次渲染 public void requestRender(); //请求一次渲染,并等待渲染完成 public void requestRenderAndWait(); //创建Surface public void surfaceCreated(); //销毁Surface public void surfaceDestroyed(); public void onPause(); public void onResume(); //Surface的大小被改变时调用 public void onWindowResize(int w, int h); //请求退出渲染线程,并等待退出 public void requestExitAndWait(); //请求是否EglContext public void requestReleaseEglContextLocked(); //向GL线程发送一个任务 public void queueEvent(Runnable r); } //debug使用的 static class LogWriter extends Writer { } //很多方法都会调用此方法,会检查mGLThread不为null //即保证调用此方法的方法,必须在setRenderer之前调用 private void checkRenderThreadState(); //主要就是用来做同步用的,利用Object的wait和notifyAll private static class GLThreadManager { }}
渲染的主要逻辑在GLThread的guardedRun()方法中,对guardedRun()方法梳理下。
private void guardedRun() throws InterruptedException { mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); mHaveEglContext = false; mHaveEglSurface = false; mWantRenderNotification = false; try { GL10 gl = null; boolean createEglContext = false; boolean createEglSurface = false; boolean createGlInterface = false; boolean lostEglContext = false; boolean sizeChanged = false; boolean wantRenderNotification = false; boolean doRenderNotification = false; boolean askedToReleaseEglContext = false; int w = 0; int h = 0; Runnable event = null; while (true) { synchronized (sGLThreadManager) { while (true) { //外部请求退出GL线程 if (mShouldExit) { return; } /*外部请求在GL线程中处理的事件没有处理完时,就优先处理这些事件*/ if (! mEventQueue.isEmpty()) { event = mEventQueue.remove(0); break; } //暂停和恢复状态变化时,onResume和onPause状态变化 boolean pausing = false; if (mPaused != mRequestPaused) { pausing = mRequestPaused; mPaused = mRequestPaused; /*GLSurfaceView的onPause和onResume都会用wait方法等待GL线程的响应,这时候主线程阻塞。此处调用notifyAll通知onPause和onResume,放弃主线程的阻塞。GLSurfaceView中其他很多方法也存在wait方法,基本与此类似 */ sGLThreadManager.notifyAll(); if (LOG_PAUSE_RESUME) { Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId()); } } // 需要释放EglContext时候执行的工作 if (mShouldReleaseEglContext) { if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL context because asked to tid=" + getId()); } stopEglSurfaceLocked(); stopEglContextLocked(); mShouldReleaseEglContext = false; askedToReleaseEglContext = true; } // EglContext丢失时,销毁EglSurface和EglContext if (lostEglContext) { stopEglSurfaceLocked(); stopEglContextLocked(); lostEglContext = false; } //接收了暂停信号,而且当前EglSurface存在时,销毁EglSurface if (pausing && mHaveEglSurface) { if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); } stopEglSurfaceLocked(); } /*接收了暂停信号,而且当前EglContext存在时,根据用户设置,来决定是否销毁EglContext*/ if (pausing && mHaveEglContext) { GLSurfaceView view = mGLSurfaceViewWeakRef.get(); boolean preserveEglContextOnPause = view == null ? false : view.mPreserveEGLContextOnPause; if (!preserveEglContextOnPause) { stopEglContextLocked(); if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); } } } /*Surface不存在(不是EglSurface),而且当前并没有在等待Surface*/ if ((! mHasSurface) && (! mWaitingForSurface)) { if (LOG_SURFACE) { Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId()); } if (mHaveEglSurface) { stopEglSurfaceLocked(); } mWaitingForSurface = true; mSurfaceIsBad = false; sGLThreadManager.notifyAll(); } // Surface存在,而且在等待Surface if (mHasSurface && mWaitingForSurface) { if (LOG_SURFACE) { Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId()); } mWaitingForSurface = false; sGLThreadManager.notifyAll(); } if (doRenderNotification) { if (LOG_SURFACE) { Log.i("GLThread", "sending render notification tid=" + getId()); } mWantRenderNotification = false; doRenderNotification = false; mRenderComplete = true; sGLThreadManager.notifyAll(); } // 当前环境准备好了渲染执行,否则进入下一轮等待及判断 if (readyToDraw()) { // 没有EglContext就需要借助EglHelper来创建EglContext if (! mHaveEglContext) { if (askedToReleaseEglContext) { askedToReleaseEglContext = false; } else { try { mEglHelper.start(); } catch (RuntimeException t) { sGLThreadManager.releaseEglContextLocked(this); throw t; } mHaveEglContext = true; createEglContext = true; sGLThreadManager.notifyAll(); } } /*有了EglContext,但是没有EglSurface,就需要设置一些状态,以便后续操作*/ if (mHaveEglContext && !mHaveEglSurface) { mHaveEglSurface = true; createEglSurface = true; createGlInterface = true; sizeChanged = true; } /*有eglSurface时,需要判断是否需要执行surface sizechange*/ if (mHaveEglSurface) { if (mSizeChanged) { sizeChanged = true; w = mWidth; h = mHeight; mWantRenderNotification = true; if (LOG_SURFACE) { Log.i("GLThread", "noticing that we want render notification tid=" + getId()); } // Destroy and recreate the EGL surface. createEglSurface = true; mSizeChanged = false; } mRequestRender = false; sGLThreadManager.notifyAll(); if (mWantRenderNotification) { wantRenderNotification = true; } //注意此处break,跳出等待的循环 break; } } // By design, this is the only place in a GLThread thread where we wait(). if (LOG_THREADS) { Log.i("GLThread", "waiting tid=" + getId() + " mHaveEglContext: " + mHaveEglContext + " mHaveEglSurface: " + mHaveEglSurface + " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface + " mPaused: " + mPaused + " mHasSurface: " + mHasSurface + " mSurfaceIsBad: " + mSurfaceIsBad + " mWaitingForSurface: " + mWaitingForSurface + " mWidth: " + mWidth + " mHeight: " + mHeight + " mRequestRender: " + mRequestRender + " mRenderMode: " + mRenderMode); } sGLThreadManager.wait(); } } /*外部请求在GL线程中处理的事件没有处理完时,就优先处理这些事件*/ if (event != null) { event.run(); event = null; continue; } //后续就是根据上面的判断设置,来执行相应的操作 if (createEglSurface) { if (LOG_SURFACE) { Log.w("GLThread", "egl createSurface"); } //创建EglSurface if (mEglHelper.createSurface()) { synchronized(sGLThreadManager) { mFinishedCreatingEglSurface = true; sGLThreadManager.notifyAll(); } } else { synchronized(sGLThreadManager) { mFinishedCreatingEglSurface = true; mSurfaceIsBad = true; sGLThreadManager.notifyAll(); } continue; } createEglSurface = false; } if (createGlInterface) { gl = (GL10) mEglHelper.createGL(); createGlInterface = false; } if (createEglContext) { if (LOG_RENDERER) { Log.w("GLThread", "onSurfaceCreated"); } GLSurfaceView view = mGLSurfaceViewWeakRef.get(); if (view != null) { try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated"); //调用GLSurfaceView设置的renderer的onSurfceCreated方法 view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } createEglContext = false; } //surface大小被改变 if (sizeChanged) { if (LOG_RENDERER) { Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); } GLSurfaceView view = mGLSurfaceViewWeakRef.get(); if (view != null) { try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged"); view.mRenderer.onSurfaceChanged(gl, w, h); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } sizeChanged = false; } if (LOG_RENDERER_DRAW_FRAME) { Log.w("GLThread", "onDrawFrame tid=" + getId()); } //每帧绘制 { GLSurfaceView view = mGLSurfaceViewWeakRef.get(); if (view != null) { try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame"); view.mRenderer.onDrawFrame(gl); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } } int swapError = mEglHelper.swap(); switch (swapError) { case EGL10.EGL_SUCCESS: break; case EGL11.EGL_CONTEXT_LOST: if (LOG_SURFACE) { Log.i("GLThread", "egl context lost tid=" + getId()); } lostEglContext = true; break; default: EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError); synchronized(sGLThreadManager) { mSurfaceIsBad = true; sGLThreadManager.notifyAll(); } break; } if (wantRenderNotification) { doRenderNotification = true; wantRenderNotification = false; } } } finally { /* * clean-up everything... */ synchronized (sGLThreadManager) { stopEglSurfaceLocked(); stopEglContextLocked(); } }}
SurfaceView/TextureView或Pbuffer上的OpenGLES渲染
根据上面的分析,我们知道,GLSurfaceView有setEGLWindowSurfaceFactory
借助此方法,我们可以将图像渲染到其他的地方,比如我们创建一个如下的自定义GLSurfaceView,就可以将图像渲染到外部指定surface上。但是遗憾的是,在某些手机上,这种方式会失效。
private class GLView extends GLSurfaceView{ public GLView(Context context) { super(context); init(); } private void init(){ getHolder().addCallback(null); setEGLWindowSurfaceFactory(new GLSurfaceView.EGLWindowSurfaceFactory() { @Override public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object window) { return egl.eglCreateWindowSurface(display,config,surface,null); } @Override public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { egl.eglDestroySurface(display, surface); } }); setEGLContextClientVersion(2); setRenderer(TextureController.this); setRenderMode(RENDERMODE_WHEN_DIRTY); setPreserveEGLContextOnPause(true); } public void attachedToWindow(){ super.onAttachedToWindow(); } public void detachedFromWindow(){ super.onDetachedFromWindow(); }}
那么如何办呢?我们可以新建一个类GLEnvironment,将GLSurfaceView内容全部复制出来,然后取消其继承和接口实现,将所有报错的代码删除掉,这样就相当于剔除了GLSurfaceView的SurfaceView而保留了它的GL环境,我们可以使用GLEnvironment来进行渲染,并自由的指定渲染载体,可以是SurfaceView/TextureView或Pbuffer,也可以是Pixmap。
同样是利用setEGLWindowSurfaceFactory
方法来设置,当然可以改个名字更为贴切,比如setEGLSurfaceFactory,如下:
mEnv.setEGLSurfaceFactory(new GLEnvironment.EGLSurfaceFactory() { @Override public EGLSurface createSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) { /*使用SurfaceView或者TextureView,customWindow可以为SurfaceTexture\SurfaceHolder或者Surface等*/ egl.eglCreateWindowSurface(display,config,customWindow,null); //使用pbuffer //reture egl.eglCreatePbufferSurface(); //使用pixmap //return egl.eglCreatePixmapSurface(); } @Override public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) { egl.eglDestroySurface(display,surface); }});
具体代码请参考AndroidOpenGLDemo,在其中搜索GLEnvironment。
更多相关文章
- Android学习笔记-ProgressBar和ListView使用方法(二)
- Android getResources().getConfiguration()方法的作用
- 快速搭建Android 开发环境-使用ADT Bundle
- 【Android】UI界面外的线程,控制刷新UI界面
- 我的Android环境搭建
- Android读取文件方法总结