GLSurfaceView 基本使用与源码解析

   日期:2020-08-27     浏览:87    评论:0    
核心提示:GLSurfaceView 的基本使用 在Android 中使用OpenGL ES的过程中,就不得不提到一个控件:GLSurfaceView,本篇博文将结合几个简单的案例分别介绍GLSurfaceView的基本使用以及如何使用GLSurfaceView.Renderer所提供的接口将图形绘制到SurfaceView中。 OpenGL(Open Graphics Library)意为开放图形库,是一个跨平台的图形API,用于指定3D图形处理硬件中的软硬件编程接口。OpenGL一...

GLSurfaceView 的基本使用

       在Android 中使用OpenGL ES的过程中,就不得不提到一个控件:GLSurfaceView,本篇博文将结合几个简单的案例分别介绍GLSurfaceView的基本使用以及如何使用GLSurfaceView.Renderer所提供的接口将图形绘制到SurfaceView中。

       OpenGL(Open Graphics Library)意为开放图形库,是一个跨平台的图形API,用于指定3D图形处理硬件中的软硬件编程接口。OpenGL一般用于图形工作站,PC端使用。由于性能和可移植性等各方面原因,在移动端使用起来相对比较麻烦。为此,Khronos公司就为OpenGL提供一个子集,OpenGL ES(OpenGL for Embedded System)。OpenGL ES是免费的跨平台且功能完善的2D/3D图形库接口API,是OpenGL的一个子集。

     GLSurfaceView的特点:

      1) GLSurfaceView继承SurfaceView,并实现了SurfaceHolder.Callback2接口。拥有SurfaceView的特性。如能在View的基础上创建独立的Surface,拥有SurfaceHolder来管理Surface,并且可以在子线程中进行View的渲染操作。通过SurfaceHolder得到Canvas,然后在单独的线程中利用Canvas绘制内容,然后更新到Surface。

       2)GLSurfaceView在SurfaceView的基础上实现GLThread(EGLContext创建GL环境所在线程),可通过OpenGL进行绘制,可在SurfaceView所提供的Surface进行绘制。

       3)支持按需渲染(on-demand)和连续渲染(continuous)两种模式。

    1. GLSurfaceView 的使用步骤

       使用GLSurfaceView时, 可以使用set方法来修改默认的属性,例如setRenderer(Renderer)设置渲染器,主要的工作就是设置Renderer,其他过程,例如EGL的创建,Surface的分配以及OpenGLES的调用都被隐藏起来了,GLSurfaceView会启动一个工作线程来完成渲染,避免阻塞UI主线程,这个工作线程就是mGLThread,这个线程在应用程序setRederer的时候启动,然后不停地等待和处理事件,同时还负责开展Render工作。本文使用的是 OpenGL ES 2 版本。

       1)创建GLSurfaceView

       在布局文件文件中加入GLSurfaceView,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.opengl.GLSurfaceView
        android:id="@+id/glv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

       然后在Activity的onCreate中实例化GLSurfaceView对象,在Activity或者Fragment的onResume方法和onPause方法中,分别调用GLSurfaceView的onResume和onPause方法。代码如下所示:

public class MainActivity extends AppCompatActivity {
    GLSurfaceView glSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        glSurfaceView = findViewById(R.id.glv);

    }

    @Override
    protected void onResume() {
        super.onResume();
        glSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        glSurfaceView.onPause();
    }
}

       2)判断设备是否支持OpenGL ES2

    
    public boolean supportES2() {
        ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        ConfigurationInfo deviceConfigurationInfo = am.getDeviceConfigurationInfo();
        int reqGlEsVersion = deviceConfigurationInfo.reqGlEsVersion;
        return reqGlEsVersion >= 0x2000;
    }


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        glSurfaceView = findViewById(R.id.glv);

        if(supportES2()){
            // 设置 OpenGL ES 版本
            glSurfaceView.setEGLContextClientVersion(2);
            glSurfaceView.setRenderer(new MyRenderer());
        }else {
            Toast.makeText(this , "This device does not support OpenGL ES 2.0." ,
                    Toast.LENGTH_SHORT).show();
            return;
        }
    }

       3)初始化OpenGL ES 环境

      GLSurfaceView默认情况下已经初始化好了OpenGL ES的运行环境,不需要做额外的工作就可以使用,当然也可以更改一些默认的设置。

       4) 设置 Renderer

       渲染是OpenGL ES 的核心工作,setRenderer 可以将用户自定义的Renderer 加入实际的渲染流程中,首先我们自定义MyRenderer类实现GLSurfaceView.Renderer接口,并实现onSurfaceCreated ,onDrawFrame,onSurfaceChanged方法,GLSurfaceView的渲染工作主要由GLSurfaceView.Renderer负责渲染,代码如下所示:

public class MyRenderer implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Surface 创建时调用
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        // Surface 改变时调用
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // 执行渲染工作
        GLES20.glClearColor(GLES20.GL_COLOR_BUFFER_BIT, 0f, 0f, 0f);
    }
}

       然后将MyRenderer设置给GLSurfaceView,代码如下:

  glSurfaceView.setRenderer(new MyRenderer());

       MyRenderer 给 GLSurfaceView 渲染一层黑色背景。

 

    2. 使用GLSurfaceView绘制三角形

       自定义TriangleRenderer并实现GLSurfaceView.Renderer 接口,代码如下所示:

public class TriangleRenderer implements GLSurfaceView.Renderer {

    private int mProgram;
    private FloatBuffer vertexBuffer;

    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";

    // 三角形坐标位置。
    static float triangleCoordinate[] = {
            0.5f, 0.5f, 0.0f,       // top
            -0.5f, -0.5f, 0.0f,     // bottom left
            0.5f, -0.5f, 0.0f       // bottom right
    };

    //设置颜色,依次为红、绿、蓝、透明度
    float color[] = {1.0f, 0f, 0f, 1.0f};
    static final int COORDINATE_PER_VERTEX = 3;

    private int mPositionHandle;
    private int mColorHandle;

    //顶点个数
    private final int vertexCount = triangleCoordinate.length / COORDINATE_PER_VERTEX;
    //顶点之间的偏移量
    private final int vertexStride = COORDINATE_PER_VERTEX * 4; // 每个顶点四个字节


    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
        //将背景设置为灰色
        gl10.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        //申请底层空间
        ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoordinate.length * 4);
        bb.order(ByteOrder.nativeOrder());
        //将坐标数据转换为FloatBuffer,用以传入OpenGL ES程序
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(triangleCoordinate);
        vertexBuffer.position(0);

        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        //创建一个空的OpenGLES程序
        mProgram = GLES20.glCreateProgram();
        //将顶点着色器加入到程序
        GLES20.glAttachShader(mProgram, vertexShader);
        //将片元着色器加入到程序中
        GLES20.glAttachShader(mProgram, fragmentShader);
        //连接到着色器程序
        GLES20.glLinkProgram(mProgram);
    }

    public int loadShader(int type, String shaderCode) {
        //根据type创建顶点着色器或者片元着色器
        int shader = GLES20.glCreateShader(type);
        //将资源加入到着色器中,并编译
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);
        return shader;
    }


    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        //将程序加入到OpenGLES2.0环境
        GLES20.glUseProgram(mProgram);
        //获取顶点着色器的vPosition成员句柄
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //启用三角形顶点的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //准备三角形的坐标数据
        GLES20.glVertexAttribPointer(mPositionHandle, COORDINATE_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        //获取片元着色器的vColor成员的句柄
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        //设置绘制三角形的颜色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        //绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}

         使用 TriangleRenderer,代码如下:

  glSurfaceView.setRenderer(new TriangleRenderer());

       运行效果如下:

    2. GLSurfaceView源码解析 

       本文是基于 Android -29 来解析 GLSurfaceView 的源码。

     2.1 入口 setRenderer 方法。

       我们先从 setRenderer(Renderer renderer) 这个方法开始分析,代码如下所示:

  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();
        }
        // 将传入的 renderer 赋值给GLSurfaceView的成员变量 mRenderer 
        mRenderer = renderer;

        // 创建GLThread线程
        mGLThread = new GLThread(mThisWeakRef);
        // 启动GLThread线程
        mGLThread.start();
    }

       方法 checkRenderThreadState()检查了 mGLThread 是否已经存在,存在则抛出异常;换句话说,我们不能在同一个GLSurfaceView 调用多次setRenderer(Renderer renderer)。

  mEGLConfigChoosermEGLContextFactorymEGLWindowSurfaceFactory是用户在setRenderer之前,可以调用相关方法来进行EGL设置,如果没有设置则采用默认实现。

  mEGLConfigChooser主要是指定了OpenGL ES一些颜色深度、缓存区深度的一些设定。
  mEGLContextFactory主要是提供了创建和销毁EGLContext上下文的一些处理。
  mEGLWindowSurfaceFactory主要是提供了创建和销毁EGLSurface的一些处理。

  GLThread就是我们一直所说的GL线程,主要是用于与OpenGL ES环境进行交互的线程。入参mThisWeakRef是一个弱引用,指向了GLSurfaceView本身。

       接下来我们分析一下GLThread这个GL线程,首先查看 GLThread 的 run 方法,代码如下所示:

       public void run() {
            setName("GLThread " + getId());
            if (LOG_THREADS) {
                Log.i("GLThread", "starting tid=" + getId());
            }

            try {
                guardedRun();
            } catch (InterruptedException e) {
                // fall thru and exit normally
            } finally {
                sGLThreadManager.threadExiting(this);
            }
        }

       在 run 方法中调用了 guardedRun方法,下面我们来查看 guardedRun 方法中的主要逻辑

       1)创建一个 EglHelper 对象,EglHelper是一个封装了一些EGL通用操作的工具类。

   mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);

        2)第一次循环时,会执行如下代码:

    // If we don't have an EGL context, try to acquire one.
    if(!mHaveEglContext){
        if (askedToReleaseEglContext) {
            askedToReleaseEglContext = false;
        } else {
            try {
                // 开始初始化 OpenGL ES环境
                mEglHelper.start();
            } catch (RuntimeException t) {
                sGLThreadManager.releaseEglContextLocked(this);
                throw t;
            }
            mHaveEglContext = true;
            createEglContext = true;

            sGLThreadManager.notifyAll();
        }
    }

       通过调用EglHelper.start()进行OpenGL ES环境的初始化。然后将标示量mHaveEglContext 、createEglContext设置为true。

       下面我们查看 EglHelper 的 start 方法,代码如下所示:

    public void start() {
        ...
        // 获取一个EGL实例
        mEgl = (EGL10) EGLContext.getEGL();

        // 获取EGL显示设备
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

        // 判断EGL显示设备是否正常
        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }

        // 存放EGL的主版本号和次版本号
        int[] version = new int[2];
        if (!mEgl.eglInitialize(mEglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed");
        }
        // 获取GLSurfaceView 的应用。
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view == null) {
            mEglConfig = null;
            mEglContext = null;
        } else {
            // 配置 EGLConfig 和 EGLContext
            mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
            mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); // ... 1
        }
        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;
    }

       在注释1处,如果我们没有调用 setEGLContextFactory 这个方法(除非特殊需求,一般情况是不会调用),那么GLSurfaceView 会默认初始化 EGLContextFactory 为 DefaultContextFactory 类的对象。默认初始化是在 setRenderer方法中实现的。下面我们分析 DefaultContextFactory 类的 createContext 方法,代码如下所示:

//最简单的创建EGLContext的方式,如果需要用到多线程共享一个OpenGL ES环境的话,需要自己实现这个方法处理     
  public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {  
          int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
                    EGL10.EGL_NONE };
          return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
                    mEGLContextClientVersion != 0 ? attrib_list : null);
  }

       OpenGL ES环境的初始化就完成了。

       回到GLThread 的 guardedRun 方法,代码如下:

   //  当OpenGL ES环境初始化完成之后将createEglSurface和createGlInterface都设置为true
   if(mHaveEglContext &&!mHaveEglSurface){
        mHaveEglSurface = true;
        createEglSurface = true;
        createGlInterface = true;
        sizeChanged = true;
    }

    //由上面代码可知 mHaveEglSurface == true
    if(mHaveEglSurface){
        //mSizeChanged是在SurfaceView回调surfaceChanged会设置会true
        //首次初始化mSizeChanged默认为true
        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;
    }

       上面的代码中我们主要关注 createEglSurface 变量,接下来我们分析下面这段代码:

    if(createEglSurface){
        if (LOG_SURFACE) {
            Log.w("GLThread", "egl createSurface");
        }
        if (mEglHelper.createSurface()) { // ... 1
            synchronized (sGLThreadManager) {
                mFinishedCreatingEglSurface = true;
                sGLThreadManager.notifyAll();
            }
        } else {
            synchronized (sGLThreadManager) {
                mFinishedCreatingEglSurface = true;
                mSurfaceIsBad = true;
                sGLThreadManager.notifyAll();
            }
            continue;
        }
        createEglSurface = false;
    }

        在注释1处调用 EglHelper 的 createSurface 方法,代码如下所示:

    public boolean createSurface() {
        ...
        // 如果已经创建过EglSurface,这里就先销毁
        destroySurfaceImp();

        // 获取GLSurfaceView的引用
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            //调用mEGLWindowSurfaceFactory工程创建一个EglSurface
            //如果我们没有指定,会默认触发DefaultWindowSurfaceFactory的逻辑
            mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
                    mEglDisplay, mEglConfig, view.getHolder());
        } else {
            mEglSurface = null;
        }

        // 检测是否创建EglSurface是否成功
        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;
        }
        // 将EglContext上下文加载到当前线程环境
        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            
            logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
            return false;
        }

        return true;
    }

       继续查看 DefaultWindowSurfaceFactorycreateWindowSurface()方法,代码如下所示:

        public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
                EGLConfig config, Object nativeWindow) {
            EGLSurface result = null;
            try {
                result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
            } catch (IllegalArgumentException e) {
                Log.e(TAG, "eglCreateWindowSurface", e);
            }
            return result;
        }

       我们可以通过自己实现createWindowSurface()方法实现我们需要的EGLSurface创建方式。

       然后查看  destroySurfaceImp 方法的具体实现,代码如下:

    private void destroySurfaceImp() {
        if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
            // 清理当前线程的EglContext 上下文环境
            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_SURFACE,
                    EGL10.EGL_NO_CONTEXT);
            // 获取GLSurfaceView的引用
            GLSurfaceView view = mGLSurfaceViewWeakRef.get();
            if (view != null) {
                // 销毁前面创建出来的EGLSurface
                view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
            }
            mEglSurface = null;
        }
    }

  DefaultWindowSurfaceFactory 的逻辑就分析完了。

       接下来我们回到 GLThread 的 guardedRun 方法,可以看到 createGlInterface 这个标示量的代码逻辑块,代码如下:


    if(createGlInterface){
        gl = (GL10) mEglHelper.createGL();

        createGlInterface = false;
    }

       跟进查看 EglHelper 的 createGL 方法,代码如下:

    GL createGL() {
        // 获取 OpenGL ES 的编程接口
        GL gl = mEglContext.getGL();
        // 获取GLSurfaceView 的引用
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            //如果我们没调用setGLWrapper(GLWrapper glWrapper)这个接口,view.mGLWrapper == null
            if (view.mGLWrapper != null) {
                gl = view.mGLWrapper.wrap(gl);
            }
           ...
        }
        return gl;
    }

  接下来我们回到 GLThread 的 guardedRun 方法,可以看到 createEglContext 这个标示量的代码逻辑块,代码如下所示:

    if(createEglContext){
        if (LOG_RENDERER) {
            Log.w("GLThread", "onSurfaceCreated");
        }
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");
                view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); // ... 1 
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
        createEglContext = false;
    }

       在注释1处会回调到上层自定义的Renderer的onSurfaceCreated()方法,说明GLSurfaceView的GL环境已经准备完毕了。

       接下来我们回到 GLThread 的 guardedRun 方法,可以看到 sizeChanged 这个标示量的代码逻辑块,代码如下所示:

        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); // ... 1
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_VIEW);
                }
            }
            sizeChanged = false;
        }

       在注释1处会回调到上层自定义的Renderer的onSurfaceChanged(),标识GLSurfaceView的宽高已经发生改变。

      上面整个流程已经将GLSurfaceView的EGL环境准备完毕了,接下来我们看看是怎么触发渲染的。

       渲染业务还是在 GLTread 的 guardedRun 方法中,代码如下所示:

    {
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            try {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
                view.mRenderer.onDrawFrame(gl); // ... 1
                if (finishDrawingRunnable != null) {
                    finishDrawingRunnable.run();
                    finishDrawingRunnable = null;
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    }

       在注释1处回调到上层Renderer的onDrawFrame()实现,也就是说就能开始OpenGL ES的绘制工作。

      2. 2 刷新模式

   GLSurfaceView有两种刷新模式:RENDERMODE_WHEN_DIRTY 和 RENDERMODE_CONTINUOUSLY

       首先我们先看看RENDERMODE_CONTINUOUSLY循环刷新模式。
       先看看设置入口,代码如下所示:

    public void setRenderMode(int renderMode) {
        mGLThread.setRenderMode(renderMode);
    }

       继续跟踪 GLThread 的 setRenderMode 方法,代码如下所示:

    public void setRenderMode(int renderMode) {
        // 对传入的 renderMode 进行校验
        if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
            throw new IllegalArgumentException("renderMode");
        }
        synchronized(sGLThreadManager) { 
            // 将传入的 renderMode 赋值给 GLThread 的成员变量 mRenderMode
            mRenderMode = renderMode;
            sGLThreadManager.notifyAll();
        }
    }

       搜索一下代码,我们发现mRenderMode在这个readyToDraw()方法使用到,代码如下所示:

    private boolean readyToDraw() {
        //需要非暂停状态、EGLSurface已经创建、并宽高不为0
        return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
                && (mWidth > 0) && (mHeight > 0)
                //如果我们设置RENDERMODE_CONTINUOUSLY的话 这条件是成立的!
                && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
    }

       继续搜索代码查看readyToDraw调用的地方,我们发现在 GLThread 的 guardedRun 方法中,代码如下所示:

     while (true) {
            if (readyToDraw()) {
                if (!mHaveEglContext) {
                    //......
                }

                if (mHaveEglContext && !mHaveEglSurface) {
                    //......
                }

                if (mHaveEglSurface) {
                    //......
                    //触发一次后重置为false。
                    mRequestRender = false;
                    //......
                    break;
                }
            }
            //......
            sGLThreadManager.wait();
        }
        //......
        GLSurfaceView view = mGLSurfaceViewWeakRef.get();
        if (view != null) {
            try {
                view.mRenderer.onDrawFrame(gl);
            } finally {
            }
        }

       可以得知,如果readyToDraw()返回true的话,逻辑会走到break退出当前while循环。自然而然会调用到我们上层OpenGL ES的渲染实现。

       如果mRenderMode == RENDERMODE_CONTINUOUSLY的话,那么readyToDraw()在成功初始化后返回 true。也就是在子线程运行时间里会不断while循环调用view.mRenderer.onDrawFrame()这个回调。

       那么当mRenderMode的值为RENDERMODE_WHEN_DIRTY时,由于mRequestRender默认为true,readyToDraw()第一次返回了true,但是在GLThread 的 guardedRun 方法中的 if(readyToDraw())代码中赋值为 false了,那么之后 readyToDraw()方法返回了false,然后逻辑走到了sGLThreadManager.wait()导致线程阻塞。

       很明显,我们需要看看mRequestRender这个变量被赋值的位置,例如 requestRender 方法,代码如下所示:

    public void requestRender() {
        synchronized(sGLThreadManager) {
            //重点这里,将mRequestRender设置为true,也就是说接下来readyToDraw()返回true
            mRequestRender = true;
            //通知guardedRun()里面的sGLThreadManager.wait()解除阻塞继续运行
            sGLThreadManager.notifyAll();
        }
    }

       可以得出结论,在RenderMode的值为RENDERMODE_WHEN_DIRTY时,我们调用一次requestRender()的话,相对应的就能触发一次onDrawFrame()进行OpenGL的渲染。

     关于初始化和渲染触发的源码流程分析,就是上面这些了。

    2.3 暂停与恢复

       一般来说,GLSurfaceView 需要跟随Activity 或者 Fragment 的生命周期调用对应的onPause()onResume()方法,代码最终会调用到GLThead类的相关方法,

      我们先看 onPause 方法,代码如下所示:

 public void onPause() {
        synchronized (sGLThreadManager) {
            if (LOG_PAUSE_RESUME) {
                Log.i("GLThread", "onPause tid=" + getId());
            }
            //将要暂停的状态标示量
            mRequestPaused = true;
            //通知GL线程解除阻塞
            sGLThreadManager.notifyAll();
            //这里是为了保证onPause()调用完毕后,GL线程已经是Paused状态了
            while ((!mExited) && (!mPaused)) {
                if (LOG_PAUSE_RESUME) {
                    Log.i("Main thread", "onPause waiting for mPaused.");
                }
                try {                
                    sGLThreadManager.wait();
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

       接下来我们看 onResume 方法,代码如下所示:

    public void onResume() {
        synchronized (sGLThreadManager) {
            if (LOG_PAUSE_RESUME) {
                Log.i("GLThread", "onResume tid=" + getId());
            }
            //解除暂停的状态标示量
            mRequestPaused = false;
            //顺便请求重绘一次GL
            mRequestRender = true;
            mRenderComplete = false;
            // 通知GL线程解除阻塞
            sGLThreadManager.notifyAll();
            //这里是为了保证onResume()调用完毕后,GL线程已经是Resume状态了
            while ((! mExited) && mPaused && (!mRenderComplete)) {
                if (LOG_PAUSE_RESUME) {
                    Log.i("Main thread", "onResume waiting for !mPaused.");
                }
                try {
                    sGLThreadManager.wait();
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

       我们看看这个状态是怎么工作的,还是在guardedRun()这个方法里面,代码如下所示:

        boolean pausing = false;
        // 如果 mPaused != mRequestPaused 为true ,说明调用了暂停。
        if (mPaused != mRequestPaused) {
            pausing = mRequestPaused;
            mPaused = mRequestPaused;
            sGLThreadManager.notifyAll();
            if (LOG_PAUSE_RESUME) {
                Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
            }
        }


        // 如果调用了暂停并且 EglSurface 已经创建
        if (pausing && mHaveEglSurface) {
            if (LOG_SURFACE) {
                Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
            }
            //销毁 EglSurface,并将mHaveEglSurface设置false
            stopEglSurfaceLocked();
        }

        // 如果调用了暂停并且 EglContext 已经创建
        if (pausing && mHaveEglContext) {
            //销毁 EglContext,并将mHaveEglContext设置false
            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());
                }
            }
        }

   stopEglSurfaceLocked()最终会回调到destroySurfaceImp()方法,stopEglContextLocked()最终会回调到EglHelperfinish()方法

       当我们调用了暂停时,mPaused的值是true,所以readyToDraw方法会返回false,由上面可知逻辑将会阻塞住等待唤醒。

       关于onResume()就简单了,就是一次重新的EGL环境初始化过程,这里不做详细分析。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服