1. Background

It was my second day of the August Challenge. GLSurfaceView is an important component of OpenGL ES drawing on the Android side. If we understand the source code, we can understand OpenGL ES more deeply.

2. Code analysis

2.1. The entrance

When we usually use the GLSurfaceView, the main use of two methods are setEGLContextClientVersion and setRenderer, the former is to set up OpenGL For the es version, the main drawing work is done in the second method, so let’s focus on the setRenderer method.

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();
}
Copy the code

This method is brief, mainly involves three objects, respectively is: mEGLConfigChooser, mEGLContextFactory, mEGLWindowSurfaceFactory.

MEGLConfigChooser is used in the Start method in the EGLHelper (described below) to generate the EGLConfig object and specify the OpenGL color and depth.

MEGLContextFactory is also used in the Start method of the EGLHelper to generate the EGLContext, providing methods for creating and destroying the EGLContext.

MEGLWindowSurfaceFactory is used to generate EGLSurfaces in the EGLHelper createSurface method and also provides methods for creating and destroying EGLSurfaces.

EGLDisplay is the class that represents the actual display device, and EGLSurface is the class that stores image information. It’s based on these two classes that GLSurfaceView implements the double-buffering mechanism.

2.2. The analysis of the GLThread

The setRenderer method eventually generates a GLThread object and starts the object. When we go into this class, we can see that it integrates Thread, so let’s focus on its run method.

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); }}Copy the code

The main one is the guardedRun() method, which is quite long, so just focus on the main code.

private void guardedRun() throws InterruptedException{ mEGLHlper = new EglHelper(mGLSurfaceViewWeakRef); / /... While (true) {//... If (readyToDraw()){// ready to render meglHelper.start (); } if (createGLInterface) { gl = (GL10) mEglHelper.createGL(); } if (createEglContext) { GLSurfaceView view = mGLSurfaceViewWeakRef.get(); view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); } if (sizeChanged) { GLSurfaceView view = mGLSurfaceViewWeakRef.get(); view.mRenderer.onSurfaceChanged(gl, w, h); } { GLSurfaceView view = mGLSurfaceViewWeakRef.get(); view.mRenderer.onDrawFrame(gl); } int swapError = mEglHelper.swap(); }}Copy the code

As you can see, all three of the methods that we normally implement when we’re custom Renderer are called here. In addition to that, the only thing we need to focus on is the mEGLHelper object. From the code above we can see that mEGLHelper is used throughout the GLSurfaceView, so we need to Take a look at the code for mEGLHelper

2.3. EGLHelper

public void start(){
     mEgl = (EGL10) EGLContext.getEGL();
     mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
     
     mEgl.eglInitialize(mEglDisplay, version)
     GLSurfaceView view = mGLSurfaceViewWeakRef.get();
     mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
     mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig)
     
}
Copy the code

The start method instantiates EGL,EGLConfig, and EGLContext, initializes EGL, and generates EGLDisplay.

public boolean createSurface() {
    GLSurfaceView view = mGLSurfaceViewWeakRef.get();
    mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
                        mEglDisplay, mEglConfig, view.getHolder());
}
Copy the code

The createSurface method is used to create an EGLSurface object.

GL createGL() { GL gl = mEglContext.getGL(); GLSurfaceView view = mGLSurfaceViewWeakRef.get(); if (view ! = null) { if (view.mGLWrapper ! = null) { gl = view.mGLWrapper.wrap(gl); } } return gl; }Copy the code

CreateGL is primarily used to createGL objects that are used as arguments to all three methods of the custom Render implementation.

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

The swap method calls the native method to exchange EGLDisplay and EGLSurface data.

3. Summary

With the above analysis, we can specify most of the process of GLSurface rendering, and by emulating this code we can use custom vectors for OpenGL rendering, such as TextureView, etc. The following diagram shows the main classes and methods involved in the GLSurface rendering process:

4. References

Audio and video development journey (ten) GLSurfaceView source code parsing &EGL environment