美文网首页AndroidWay
GUI:GLSurfaceView

GUI:GLSurfaceView

作者: 81bad73e9053 | 来源:发表于2016-11-15 21:29 被阅读599次

    1.java层使用EGL和OpengLES的两种方式

    第一种:使用GLES类,这些类会通过jni 的方式调用到c/c++层的EGL和OpenGLES(这种方式程序编写方式复杂)
    第二种:GLSurfaceView

    GLSurfaceView继承自SurfaceView,也就意味着它有所有view类的功能和属性,特别是处理事件的能力,同时GLSurfaceView也拥有OpenGlES所提供的图形处理能力

    GLSurfaceView的功能

    • 管理EGLDisplay,它表示一个显示屏
    • 管理Surface(本质是一块内存区域)
    • GLSurfaceView会创建新的线程,以使整个渲染过程不会阻塞UI主线程
    • 用户可以自定义渲染方式,如通过setRenderer设置一个Renderer

    2.GLSurfaceView的使用步骤

    第一步:创建GLSurfaceView

    GLSurfaceView也是View,可以通过布局文件的方式将它加入整棵view树中

    第二步:初始化OpenGLES环境

    GLSurfaceView默认情况下已经为开发人员搭建好了OpenGLES的运行环境,因而如果没有特别的要求,并不需要做额外的工作。开发人员可以更改一些默认的设置

    setEGLConfigChooser(boolean);
    setGLWrraper(GLWrapper);
    
    

    第三步:设置Renderer

    渲染是OpenGLES的核心工作,setRenderer可以将用户自定义的一个Renderer加入实际的渲染流程中。

    第四步:设置RenderingMode

    GLSurfaceView默认采用的是连续渲染的方式,如有需要可以通过这个方法更改渲染方式

    第五步:状态处理

    使用GLSurfaceView需要注意程序的声明周期,Activity会有暂停和恢复等状态,为了达到最优效果,GLSurfaceView也需根据这些状态来做相应的处理,比如Activity暂停时需要GLSurfaceView的onPause,恢复时需要GLSurfaceView的onResume等,这样能使OpenGLES的内部线程做出正确的判断,从而保证应用程序的稳定性

    开发基于GLSurfaceView的程序,主要的工作就是设置Renderer,其他过程,例如EGL的创建,Surface的分配以及OpenGLES的调用都被隐藏起来了。

    3.GLSurfaceView的创建过程

    public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
    
        public GLSurfaceView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
        
         private void init() {
            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed
            SurfaceHolder holder = getHolder();
            holder.addCallback(this); //添加回调,这样surface有变化的时候就能收到通知了
        }
        //..........    
    }   
    

    3.1 Callback

        public interface Callback {
            /**当成功申请到一个Surface的时候被调用,一般情况下只会发生一次
             * This is called immediately after the surface is first created.
             * Implementations of this should start up whatever rendering code
             * they desire.  Note that only one thread can ever draw into
             * a Surface, so you should not draw into the Surface here
             * if your normal rendering will be in another thread.
             */
            public void surfaceCreated(SurfaceHolder holder);
    
            /**当Surface改变时调用,如format和size的更动
             * This is called immediately after any structural changes (format or
             * size) have been made to the surface.  You should at this point update
             * the imagery in the surface.  This method is always called at least
             * once, after surfaceCreated
             */
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                    int height);
    
            /**当Surface销毁时调用
             * This is called immediately before a surface is being destroyed. After
             * returning from this call, you should no longer try to access this
             * surface.  If you have a rendering thread that directly accesses
             * the surface, you must ensure that thread is no longer touching the 
             * Surface before returning from this function
             */
            public void surfaceDestroyed(SurfaceHolder holder);
        }
    

    3.2SurfaceHolder的创建

         private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
            //....
            public void addCallback(Callback callback) {
                synchronized (mCallbacks) { 
                    if (mCallbacks.contains(callback) == false) {      
                        mCallbacks.add(callback);
                    }
                }
            }
         //....
         }
    

    SurfaceView中,mSurfaceHolder 是一个全局变量,addCallback用于将接受回调通知的类加入到mCallbacks队列中,如GLSurfaceView在构造方法中调用addCallback方法,这样当SurfaceHolder有变化的时候就能通知到GLSurfaceView。

    3.3 SurfaceView中surface的申请(与一般的View不同)

    TU 17-11SurfaceView中Surface的申请过程

        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            mParent.requestTransparentRegion(this);
            //mSession是ViewRoot与WMS的通信中介
            mSession = getWindowSession();
            mLayout.token = getWindowToken();
            mLayout.setTitle("SurfaceView");
            //...
        }
    
        
        private void updateWindow(boolean force, boolean redrawNeeded) {  
            ViewRootImpl viewRoot = (ViewRootImpl) getRootView().getParent();
            relayoutResult = mSession.relayout(
                            mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                                visible ? VISIBLE : GONE,
                                WindowManagerImpl.RELAYOUT_DEFER_SURFACE_DESTROY,
                                mWinFrame, mContentInsets,
                                mVisibleInsets, mConfiguration, mNewSurface);        
        mSurface.transferFrom(mNewSurface);
        }   
    

    1.一般情况下,SurfaceView是布局在某个ViewGroup中的,当ViewRoot成功AttachToWindow之后,它需要将这一事件告诉View树中的所有成员
    2.SurfaceView收到AttachToWindow成功的消息后,会通过getWindowSession方法获取一个IWindowSession,IWindowSession是ViewRoot与WMS的通信中介。
    3.SurfaceView在updateWindow的时候,利用IWindowSession.relayout()来重新申请一个Surface,其中最后一个参数就是WMS新生成的Surface。
    4.SurfaceView获取到新的Surface之后,会Transfer到mSurface变量中,然后再通知所有注册了Callback的对象,如GLSurfaceView。这样SurfaceView的Surface就创建成功了。

    3.4Surface申请成功之后,GLSurfaceView的操作

        public void surfaceCreated(SurfaceHolder holder) {
            mGLThread.surfaceCreated();
        }
    
    public void surfaceCreated() {
        synchronized(sGLThreadManager) {
             
            mHasSurface = true;
            mFinishedCreatingEglSurface = false;
            sGLThreadManager.notifyAll();
            while (mWaitingForSurface
                   && !mFinishedCreatingEglSurface
                   && !mExited) { 
                    sGLThreadManager.wait();                    
            }
        }
    }
    

    GLSurfaceView会启动一个工作线程来完成渲染,避免阻塞UI主线程,这个工作线程就是mGLThread,这个线程在应用程序setRederer的时候启动,然后不停地等待和处理事件,同时还负责开展Render工作。

    public void setRenderer(Renderer renderer) {
        checkRenderThreadState(); 
        mRenderer = renderer;
        //mGLThread在setRenderer时初始化并启动
        mGLThread = new GLThread(mThisWeakRef);
        mGLThread.start();
    }
    
    static class GLThread extends Thread {
        @Override
        public void run() { 
            guardedRun();
        }
    }
    
    

    3.5 guardedRun

    private void guardedRun() throws InterruptedException {
            while (true) {//死循环,除非主动跳出
                synchronized (sGLThreadManager) {
                    while (true) {
                        if (mShouldExit) {//是否需要结束循环
                            return;
                        }
                        //从EventQueue中取出消息
                        if (! mEventQueue.isEmpty()) {
                            event = mEventQueue.remove(0);
                            break;
                        }
    
    
                        // When pausing, release the EGL surface:
                        if (pausing && mHaveEglSurface) {
                            //如果acitivity已经暂停,释放surface
                            stopEglSurfaceLocked();
                        }
    
                        // When pausing, optionally release the EGL Context:
    
                        // Have we lost the SurfaceView surface?
                        //判断Surface是否丢失
                        if ((! mHasSurface) && (! mWaitingForSurface)) {
                            //如果当前没有Surface,而且也不再等待Surface的创建,说明已经失去了Surface
                            if (mHaveEglSurface) {
                                stopEglSurfaceLocked();
                            }
                            mWaitingForSurface = true;
                            mSurfaceIsBad = false;
                            sGLThreadManager.notifyAll();
                        }
    
                        // Have we acquired the surface view surface?
                        if (mHasSurface && mWaitingForSurface) {
                            //如果已经成功获取到Surface,那就重新设置相应的全局变量
                            mWaitingForSurface = false;
                            //通知任何在等待的线程
                            sGLThreadManager.notifyAll();
                        }
    
                        // Ready to draw?
                        if (readyToDraw()) {
                        //这里是最核心的工作,根据应用程序设置的Renderer来进行图形渲染
                        } 
                        sGLThreadManager.wait();
                    }
                } // end of synchronized(sGLThreadManager)
    
                if (event != null) {
                    event.run();
                    event = null;
                    continue;
                }
    
                if (createEglSurface) {//EglSurface需要被创建
                    //.....创建
                    //创建成功之后设置标志位
                    createEglSurface = false;
                }
                
                if (sizeChanged) { 
                    GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                    if (view != null) {//通知应用程序发生了变化
                        view.mRenderer.onSurfaceChanged(gl, w, h);
                    }
                    sizeChanged = false;
                } 
                
                {
                    GLSurfaceView view = mGLSurfaceViewWeakRef.get();
                    if (view != null) {//调用应用程序的Renderer
                        view.mRenderer.onDrawFrame(gl);
                    }
                }
                //通过swap把渲染结果显示到屏幕上
                int swapError = mEglHelper.swap();
                 
            }           
    }
    
    

    第一步:判断事件队列是否为空,如果有事件需要处理,则直接跳出内循环,否则依然在内循环运行
    第二步:1.判断是否需要释放EGLSurface,2.判断是否丢失了Surface,mHasSurface表示当前有没有可用的Surface,mWaitingForSurface表示是否在申请Surface的过程中,3.是否需要放弃EGLContext
    第三步:经过以上的判断之后,程序进入图形渲染前的准备工作,也就是readyToDraw之间的代码
    第四步:一旦程序执行到这里也就是跳出了内循环,有两种可能,第一是EventQueue中有需要处理的事件,第二是需要执行渲染工作

    3.6 readyToDraw

    // Ready to draw?
    if (readyToDraw()) { 
        // If we don't have an EGL context, try to acquire one.
        if (! mHaveEglContext) {//没有EGLContext的情况下
            //判断是否要建立EGLContext
            if (askedToReleaseEglContext) {
                askedToReleaseEglContext = false;
            } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {
                try {
                    mEglHelper.start();
                } catch (RuntimeException t) {
                    sGLThreadManager.releaseEglContextLocked(this);
                    throw t;
                }
                mHaveEglContext = true;
                createEglContext = true;
    
                sGLThreadManager.notifyAll();
            }
        }
        if (mHaveEglSurface) {//有EGLSurface      
            mRequestRender = false;
            sGLThreadManager.notifyAll();
            break;
        }
    }
    

    判断两个关键因素:EGLContext和EGLSurface是否存在并有效,如果没有EGLContext,就需要获取一个,如果当前有EGLSurface但尺寸发生了变化,那么就需要销毁他并重新申请Surface

    可以渲染图形的条件是:1.程序当前不处于pause状态2.已经成功获得Surface3.有合适的尺寸4.处于自动持续渲染状态,或者用户发起了渲染请求

    3.7 执行渲染工作的处理流程

    • 是否需要重新申请EGLSurface,如果尺寸发生了变化,此时createEGLSurface为true
    • 是否需要申请GLObject,此时createGLinterface为true
    • 是否需要生成EGLContext,此时createEGLContext为true
    • 尺寸是否变化,sizeChanged为true,此时需要通知观察者
    • 一切准备好之后,调用view.mRenderer.onDrawFrame进行真正的渲染
    • 最后通过swap把渲染结果显示到屏幕

    3.8 EGLHelper

    readyToDraw中调用了start方法,然后跳出readyToDraw后调用了createSurface方法

    第一步:

    public void start() {
        mEgl = (EGL10) EGLContext.getEGL();//获取一个EGL实例
            //获取一个EGLDisplay
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    
           //初始化EGL并返回版本号
        if(!mEgl.eglInitialize(mEglDisplay, version)) {
     
        }
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
            //选取一个配置
        mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
            //创建EGLContext
        mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
        mEglSurface = null;
    }
    

    第二步:mEglHelper.createSurface()

    
    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");
        } 
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        
        mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
                    mEglDisplay, mEglConfig, view.getHolder()); 
    
        return true;
    }
    
    
    
    private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
    
        public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
                EGLConfig config, Object nativeWindow) {
            EGLSurface result = null;
            result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); 
            return result;
    }
    
    }
    

    第三步:

    public int swap() {
                if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
                    return mEgl.eglGetError();
                }
                return EGL10.EGL_SUCCESS;
            }
    

    4.总结

    GLSurfaceView使用EGL的流程

    1.生成一个EGL实例
    mEgl = (EGL10) EGLContext.getEGL();
    2.获取一个EGL Display   
    mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
    
     3.初始化EGL并返回版本号
    if(!mEgl.eglInitialize(mEglDisplay, version)) {
     
        }
    
     4.选取一个配置
    mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
    5.创建一个EGLContext
    mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
    6.创建EGLSurface
    egl.eglCreateWindowSurface(display, config, nativeWindow, null);
    7.通过swap将渲染内容显示到屏幕
    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)
            
    
    

    wms2
    http://blog.csdn.net/xuesen_lin/article/details/8954748

    相关文章

      网友评论

        本文标题:GUI:GLSurfaceView

        本文链接:https://www.haomeiwen.com/subject/qiylpttx.html