转载自:https://blog.csdn.net/Darlingqiang/article/details/80274558
GLSurfaceView类是继承自SurfaceView的,并且实现了SurfaceHolder.Callback2接口。
GLSurfaceView内部管理着一个surface,专门负责OpenGL渲染。GLSurfaceView内部通过GLThread和EGLHelper
为我们完成了EGL环境渲染和渲染线程的创建及管理,使我们只需要在外部实现渲染器Renderer
即可使用OpenGL ES进行绘图。所以,了解GLSurfaceView的内部逻辑对于我们使用OpenGL ES来绘图还是很有必要的。
GLSurfaceView的初始化
首先,我们在最外使用GLSurfaceView的时候,实例化GLSurfaceView时其会对自身进行初始化:
1 2 3 4 | private void init() { SurfaceHolder holder = getHolder(); holder.addCallback(this); } |
可以看到这里主要对自身的Holder设置了Callback2接口,然后在自身内部实现了Callback2的四个回调方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void surfaceCreated(SurfaceHolder holder) { mGLThread.surfaceCreated(); } public void surfaceDestroyed(SurfaceHolder holder) { mGLThread.surfaceDestroyed(); } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { mGLThread.onWindowResize(w,h); } public void surfaceRedrawNeeded(SurfaceHolder holder) { if(mGLThread != null) { mGLThread.requestRenderAndWait(); } } |
可以看到四个回调方法最终都共同指向了GLThread,其实这里的GLThread是GLSurfaceView内部的绘制线程,
使绘制工作可以独立于主线程(GLThread承担了SurfaceView中大部分的工作,具体的后面再细说)。
至此,GLSurfaceView的初始化工作完成,但到这里渲染环境的初始化工作却还没有开始。
当实例化GLSurfaceView后,接着都会进行两个步骤,那就是设置OpenGL的版本号和渲染器:
1 2 | this.setEGLContextClientVersion(2); //设置OpenGL的版本号2.0 setRenderer(new MyRenderer()); //设置OpenGL的渲染器 |
这里重点要看setRenderer()这个方法,通过这个方法我们不仅为GLSurfaceView设置了renderer,
还会对EGL环境进行初步的设置工作,最后还有调起GLThread,开启绘制线程。所以在GLSurfaceView的整个生命周期中,
setRenderer()只能被调用一次。源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(); } |
设置渲染器时,首先会对渲染线程的状态进行检查,如果GLThread已经存在即setRenderer已经被调用过,
则抛出异常,这也是为什么setRenderer()只能在GLSurfaceView的整个生命周期中调用一次。
然后会对mEGLConfigChooser、mEGLContextFactory、mEGLWindowSurfaceFactory
进行实例化,
再实例化GLThread并执行该渲染线程,其中传入的mThisWeakRef
是当前GLSurfaceView的弱引用。
GLThread——GLSurfaceView内部的渲染线程
GLThread是一个继承Thread的类,主要的运行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | while(true) { synchronized(sGLThreadManager) { while(true) { ...... mEglHelper.start(); ...... sGLThreadManager.wait(); } } ...... mEglHelper.createSurface(); ...... gl = (GL10) mEglHelper.createGL(); ...... mRenderer.onSurfaceCreated(gl, mEglHelper.getEglConfig()); ...... mRenderer.onSurfaceChanged(gl, w, h); ...... mRenderer.onDrawFrame(gl); } |
其中被synchronized(sGLThreadManager)同步的代码块是用于线程之间通信的,外部线程可以停止和恢复这个线程。
这部分同步代码块的主要职责是创建EGL环境,GLThread对EGL的所有操作都是通过EglHelper来实现的。
EglHelper——创建EGL环境的帮助类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | private static class EglHelper { /** * Initialize EGL for a given configuration spec */ public void start() { ...... mEgl = (EGL10) EGLContext.getEGL(); mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); ...... mEgl.eglInitialize(mEglDisplay, version); ...... mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); } /** * Create an egl surface for the current SurfaceHolder surface. If a surfce * already exists, destroy it before creating the new surface. */ public boolean createSurface() { ...... /* * The window size has changed, so we need to create a new surface. */ destroySurfaceImp(); ...... /* * Create an EGL surface we can render into. */ mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, view.getHolder()); ...... /* * Before we can issue GL commands, we need to make sure the context * is current and bound to a surface. */ mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); ...... } /** * Create a GL object for the current EGL context */ GL createGL() { GL gl = mEglContext.getEGL(); GLSurfaceView view = mGLSurfaceViewWeakRef.get(); ...... gl = view.mGLWrapper.wrap(gl); ...... gl = GLDebugHelper.wrap(gl, configFlags, log); return gl; } ...... } |
所以整体看来,就是GLThread在其同步代码块中通过mEglHelper.start()对EGL环境进行初始化,
而后在同步块外部先调用mEglHelper.createSurface()为当前的Surface创建一个EGL的surface,
再通过mEglHelper.createGL()创建一个GL对象,最后就将GL对象传递给renderer的三个回调方法
onSurfaceCreated()、onSurfaceChanged()、onDrawFrame()
。所以我们在最外面只需要在这三个
回调方法中实现我们真正要绘制的东西即可。
再说GLThread
我们再来看看GLThread内部其他的一些方法,在最开始的时候我们说到给holder传入一个回调接口,
而这个回调接口的四个方法的实现最终都指向了GLThread的内部方法,那么我们先来看看这四个方法
在GLThread内部的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | // GLThread的内部方法 public void surfaceCreated() { synchronized(sGLThreadManager) { mHasSurface = true; mFinishedCreatingEglSurface = false; sGLThreadManager.notifyAll(); while(mWaitingForSurface && !mFinishedCreatingEglSurface && !mExited) { try { sGLThreadManager.wait(); } catch (InterruptedException e) { Thread.currentThread().interrput(); } } } } public void surfaceDestroyed() { synchronized(sGLThreadManager) { mHasSurface = true; sGLThreadManager.notifyAll(); while(!mWaitingForSurface && !mExited) { try { sGLThreadManager.wait(); } catch (InterruptedException e) { Thread.currentThread().interrput(); } } } } public void onWindowResize(int w, int h) { synchronized(sGLThreadManager) { mWidth = w; mHeight = h; mSizeChanged = true; mRequestRender = true; mRenderComplete = false; if(Thread.currentThread() == this) { return; } sGLThreadManager.notifyAll(); while(!mExited && !mPaused && !mRenderComplete && ableToDraw) { try { sGLThreadManager.wait(); } catch (InterruptedException e) { Thread.currentThread().interrput(); } } } } public void requestRenderAndWait() { synchronized(sGLThreadManager) { if(Thread.currentThread() == this) { return; } mWantRenderNotification = true; mRequestRender = true; mRenderComplete = false; sGLThreadManager.notifyAll(); while(!mExited && !mPaused && !RenderComplete && ableToDraw()) { try { sGLThreadManager.wait(); } catch (InterruptedException e) { Thread.currentThread().interrput(); } } } } |
这些供外部调用的方法其最终都只是改变GLThread的一些内部标志,然后通过sGLThreadManager.notifyAll
唤醒渲染线程,GLThread内部即通过判断这些标志位去执行对应的参数设置操作,这些参数设置操作都在
synchronized(sGLTHreadManager)同步的循环体中进行,当最后已经准备好绘制工作,则跳出内循环体,
在外循环体中调用renderer的回调方法onDrawFrame去真正实现绘制,核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | while(true) { synchronized(sGLThreadManager) { while(true) { ...... mEglHelper.start(); ...... // 如果已经准备好绘制环境 if(readyToDraw()) { // 再一次检查EglContext和EglSurface的状态,满足则跳出同步代码块的循环体 // If we don't have an EGL context, try to acquire one if(!mHaveEglContext) { ...... if(sGLThreadManager.tryAcquireEglContextLocked(this)) { mEglHelper.start(); } } if(mHaveEglSurface) { ...... break; // 跳出同步代码块的循环体 } } ...... sGLThreadManager.wait(); } } ...... // 调用Renderer进行绘制 mEglHelper.createSurface(); gl = (GL10) mEglHelper.createGL(); mRenderer.onSurfaceCreated(gl, mEglHelper.getEglConfig()); mRenderer.onSurfaceChanged(gl, w, h); mRenderer.onDrawFrame(gl); } |
最后终于回到了我们的渲染器Renderer,然后,我们只需要在外部实现Renderer的三个回调方法便
可使用OpenGL ES来绘图了。当然,在我们弄懂了GLSurfaceView的原理后自己动手封装一个也是没有
问题的。不过这里其实没有说得很明白的还有EGL这部分,后面有时间将补上。