源码分析-GLSurfaceView的内部实现
GLSurfaceView類是繼承自SurfaceView的,并且實現了SurfaceHolder.Callback2接口。
GLSurfaceView內部管理著一個surface,專門負責OpenGL渲染。GLSurfaceView內部通過GLThread和EGLHelper
為我們完成了EGL環境渲染和渲染線程的創建及管理,使我們只需要在外部實現渲染器Renderer
即可使用OpenGL ES進行繪圖。所以,了解GLSurfaceView的內部邏輯對于我們使用OpenGL ES來繪圖還是很有必要的。
GLSurfaceView的初始化
首先,我們在最外使用GLSurfaceView的時候,實例化GLSurfaceView時其會對自身進行初始化:
| 1234 | private void init() { SurfaceHolder holder = getHolder(); holder.addCallback(this);} |
可以看到這里主要對自身的Holder設置了Callback2接口,然后在自身內部實現了Callback2的四個回調方法:
| 1234567891011121314151617 | 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的版本號和渲染器:
| 12 | this.setEGLContextClientVersion(2); //設置OpenGL的版本號2.0setRenderer(new MyRenderer()); //設置OpenGL的渲染器 |
這里重點要看setRenderer()這個方法,通過這個方法我們不僅為GLSurfaceView設置了renderer,
還會對EGL環境進行初步的設置工作,最后還有調起GLThread,開啟繪制線程。所以在GLSurfaceView的整個生命周期中,
setRenderer()只能被調用一次。源碼如下:
| 123456789101112131415 | 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的類,主要的運行代碼如下:
| 1234567891011121314151617181920 | 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環境的幫助類
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657 | 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內部的實現:
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 | // 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去真正實現繪制,核心代碼如下:
| 123456789101112131415161718192021222324252627282930313233 | 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這部分,后面有時間將補上。
總結
以上是生活随笔為你收集整理的源码分析-GLSurfaceView的内部实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenCV4Android开发实录(2
- 下一篇: Android OpenGL使用GLSu