Before learning OpenGL ES, you need to understand EGL

Posted by php4hosting on Wed, 10 Nov 2021 12:30:52 +0100

What is EGL

EGL is the communication interface between OpenGL ES and Native Window System. Its main functions include:

  • Communicate with the native window system of the device;
  • Query the available types and configurations of the drawing surface;
  • Create a drawing surface;
  • Synchronous rendering between OpenGL ES and other graphics rendering API s;
  • Manage rendering resources such as texture maps.

The platform independence of OpenGL ES is realized by EGL, which shields the differences between different platforms (Apple provides its own iOS implementation of EGL API, calling itself EAGL).

The API related to the local window provides an interface to access the local window system. EGL can create the rendering surface EGLSurface and provide the graphics rendering context EGLContext for state management. Next, OpenGL ES can draw on this rendering surface.

Relationship between egl, opengles and devices

In the picture:

  • Display (EGL display) is an abstraction of the actual display device;
  • Surface (EGLSurface) is an abstraction of the memory area FrameBuffer used to store images, including Color Buffer, Stencil Buffer and Depth Buffer;
  • Context (EGLContext) stores some state information of OpenGL ES drawing;

When developing OpenGL ES applications on Android platform, class GLSurfaceView has provided us with the management of display, surface and context, that is, GLSurfaceView internally implements the encapsulation of EGL, which can easily use the implementation of interface GLSurfaceView.Renderer to render and draw using OpenGL ES API, It greatly improves the convenience of OpenGLES development.

Of course, we can also encapsulate EGL by ourselves. This paper encapsulates EGL in the Native layer, realizes image background rendering without the help of GLSurfaceView, and uses GPU to complete efficient image processing.

Application of EGL

Effect drawing of EGL background rendering

General steps for rendering with EGL:

  • Obtain the EGLDisplay object and establish a connection with the local window system Call eglGetDisplay method to get EGLDisplay.
  • Initialize EGL method After the connection is opened, the eglInitialize method is initialized.
  • Get the EGLConfig object and determine the configuration information of the rendered surface Call eglChooseConfig method to get EGLConfig.
  • Create a rendered surface EGLSurface Through EGLDisplay and EGLConfig, call eglCreateWindowSurface or eglcreatepuffersurface methods to create a rendered surface to obtain EGLSurface, where eglCreateWindowSurface is used to create on-screen rendering areas and eglcreatepuffersurface is used to create off-screen rendering areas.
  • Create rendering context EGLContext Through EGLDisplay and EGLConfig, call eglCreateContext method to create rendering context and get EGLContext.
  • Binding context Bind EGLSurface, EGLContext and EGLDisplay through eglMakeCurrent method. After the binding is successful, the OpenGLES environment is created, and then you can render.
  • Swap buffer After OpenGLES painting, use the eglSwapBuffers method to exchange the front and back buffers to display the painting content on the screen, but this method does not need to be called for off-screen rendering.
  • Release EGL environment After drawing, when EGL is no longer needed, you need to unbind eglMakeCurrent and destroy EGLDisplay, EGLSurface and EGLContext.

Code implementation:

// Create the GLES environment
int BgRender::CreateGlesEnv()
{
    // EGL config attributes
    const EGLint confAttr[] =
    {
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
            EGL_SURFACE_TYPE,EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
            EGL_RED_SIZE,   8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE,  8,
            EGL_ALPHA_SIZE, 8,// if you need the alpha channel
            EGL_DEPTH_SIZE, 8,// if you need the depth buffer
            EGL_STENCIL_SIZE,8,
            EGL_NONE
    };

    // EGL context attributes
    const EGLint ctxAttr[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
    };

    // surface attributes
    // the surface size is set to the input frame size
    const EGLint surfaceAttr[] = {
            EGL_WIDTH, 1,
            EGL_HEIGHT,1,
            EGL_NONE
    };
    EGLint eglMajVers, eglMinVers;
    EGLint numConfigs;

    int resultCode = 0;
    do
    {
        //1. Obtain the EGLDisplay object and establish a connection with the local window system
        m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if(m_eglDisplay == EGL_NO_DISPLAY)
        {
            //Unable to open connection to local windowing system
            LOGCATE("BgRender::CreateGlesEnv Unable to open connection to local windowing system");
            resultCode = -1;
            break;
        }

        //2. Initialize EGL method
        if(!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers))
        {
            // Unable to initialize EGL. Handle and recover
            LOGCATE("BgRender::CreateGlesEnv Unable to initialize EGL");
            resultCode = -1;
            break;
        }

        LOGCATE("BgRender::CreateGlesEnv EGL init with version %d.%d", eglMajVers, eglMinVers);

        //3. Obtain the EGLConfig object and determine the configuration information of the rendered surface
        if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
        {
            LOGCATE("BgRender::CreateGlesEnv some config is wrong");
            resultCode = -1;
            break;
        }

        //4. Create a rendering surface EGLSurface, and use eglCreatePbufferSurface to create an off screen rendering area
        m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConf, surfaceAttr);
        if(m_eglSurface == EGL_NO_SURFACE)
        {
            switch(eglGetError())
            {
                case EGL_BAD_ALLOC:
                    // Not enough resources available. Handle and recover
                    LOGCATE("BgRender::CreateGlesEnv Not enough resources available");
                    break;
                case EGL_BAD_CONFIG:
                    // Verify that provided EGLConfig is valid
                    LOGCATE("BgRender::CreateGlesEnv provided EGLConfig is invalid");
                    break;
                case EGL_BAD_PARAMETER:
                    // Verify that the EGL_WIDTH and EGL_HEIGHT are
                    // non-negative values
                    LOGCATE("BgRender::CreateGlesEnv provided EGL_WIDTH and EGL_HEIGHT is invalid");
                    break;
                case EGL_BAD_MATCH:
                    // Check window and EGLConfig attributes to determine
                    // compatibility and pbuffer-texture parameters
                    LOGCATE("BgRender::CreateGlesEnv Check window and EGLConfig attributes");
                    break;
            }
        }

        //5. Create rendering context EGLContext
        m_eglCtx = eglCreateContext(m_eglDisplay, m_eglConf, EGL_NO_CONTEXT, ctxAttr);
        if(m_eglCtx == EGL_NO_CONTEXT)
        {
            EGLint error = eglGetError();
            if(error == EGL_BAD_CONFIG)
            {
                // Handle error and recover
                LOGCATE("BgRender::CreateGlesEnv EGL_BAD_CONFIG");
                resultCode = -1;
                break;
            }
        }

        //6. Binding context
        if(!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglCtx))
        {
            LOGCATE("BgRender::CreateGlesEnv MakeCurrent failed");
            resultCode = -1;
            break;
        }
        LOGCATE("BgRender::CreateGlesEnv initialize success!");
    }
    while (false);

    if (resultCode != 0)
    {
        LOGCATE("BgRender::CreateGlesEnv fail");
    }

    return resultCode;
}

//Render
void BgRender::Draw()
{
    LOGCATE("BgRender::Draw");
    if (m_ProgramObj == GL_NONE) return;
    glViewport(0, 0, m_RenderImage.width, m_RenderImage.height);

    // Do FBO off screen rendering
    glUseProgram(m_ProgramObj);
    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);

    glBindVertexArray(m_VaoIds[0]);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_ImageTextureId);
    glUniform1i(m_SamplerLoc, 0);

    if (m_TexSizeLoc != GL_NONE) {
        GLfloat size[2];
        size[0] = m_RenderImage.width;
        size[1] = m_RenderImage.height;
        glUniform2fv(m_TexSizeLoc, 1, &size[0]);
    }

    //7. Rendering
    GO_CHECK_GL_ERROR();
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (const void *)0);
    GO_CHECK_GL_ERROR();
    glBindVertexArray(GL_NONE);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);

    //Once the FBO is unbound, readPixels cannot be called
    //glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);

}

//Release the GLES environment
void BgRender::DestroyGlesEnv()
{
    //8. Release EGL environment
    if (m_eglDisplay != EGL_NO_DISPLAY) {
        eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
        eglDestroyContext(m_eglDisplay, m_eglCtx);
        eglDestroySurface(m_eglDisplay, m_eglSurface);
        eglReleaseThread();
        eglTerminate(m_eglDisplay);
    }

    m_eglDisplay = EGL_NO_DISPLAY;
    m_eglSurface = EGL_NO_SURFACE;
    m_eglCtx = EGL_NO_CONTEXT;

}

The code of Java layer is mainly an ImageView, which is used to display the images before and after rendering.

// Create rendered objects
NativeBgRender mBgRender = new NativeBgRender();
// Initializing and creating the GLES environment
mBgRender.native_BgRenderInit();
// Load picture data to texture
loadRGBAImage(R.drawable.java, mBgRender);
// offscreen rendering 
mBgRender.native_BgRenderDraw();
// Read the rendered image data from the buffer and load it into ImageView
mImageView.setImageBitmap(createBitmapFromGLSurface(0, 0, 421, 586));
// Release the GLES environment
mBgRender.native_BgRenderUnInit();


private void loadRGBAImage(int resId, NativeBgRender render) {
    InputStream is = this.getResources().openRawResource(resId);
    Bitmap bitmap;
    try {
        bitmap = BitmapFactory.decodeStream(is);
        if (bitmap != null) {
            int bytes = bitmap.getByteCount();
            ByteBuffer buf = ByteBuffer.allocate(bytes);
            bitmap.copyPixelsToBuffer(buf);
            byte[] byteArray = buf.array();
            render.native_BgRenderSetImageData(byteArray, bitmap.getWidth(), bitmap.getHeight());
        }
    }
    finally
    {
        try
        {
            is.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}

private Bitmap createBitmapFromGLSurface(int x, int y, int w, int h) {
    int bitmapBuffer[] = new int[w * h];
    int bitmapSource[] = new int[w * h];
    IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
    intBuffer.position(0);
    try {
        GLES20.glReadPixels(x, y, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
                intBuffer);
        int offset1, offset2;
        for (int i = 0; i < h; i++) {
            offset1 = i * w;
            offset2 = (h - i - 1) * w;
            for (int j = 0; j < w; j++) {
                int texturePixel = bitmapBuffer[offset1 + j];
                int blue = (texturePixel >> 16) & 0xff;
                int red = (texturePixel << 16) & 0x00ff0000;
                int pixel = (texturePixel & 0xff00ff00) | red | blue;
                bitmapSource[offset2 + j] = pixel;
            }
        }
    } catch (GLException e) {
        return null;
    }
    return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
}

See the original text for the implementation code path of EGL background rendering.

recommend:

Master the basic processing of YUV image

Android OpenGL ES from getting started to mastering systematic learning tutorial

FFmpeg + OpenGLES realize audio visual playback

Little sister, is this the thin face and big eyes effect you want?

Hi little sister, is this the slimming effect you want?

To tell you the truth, I was moved by this special effect and cried

Function and use of EGL

Advanced OpenGL ES: EGL and GL threads

I think it's good. Order one and watch it~