天天看點

NDK OpenGL ES 3.0 開發(六):EGL什麼是 EGL在 Android 平台上開發 OpenGL ES 應用時,類 GLSurfaceView 已經為我們提供了對 Display , Surface , Context 的管理,即 GLSurfaceView 内部實作了對 EGL 的封裝,可以很友善地利用接口 GLSurfaceView.Renderer 的實作,使用 OpenGL ES API 進行渲染繪制,很大程度上提升了 OpenGLES 開發的便利性。EGL 的應用

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/99655635
NDK OpenGL ES 3.0 開發(六):EGL什麼是 EGL在 Android 平台上開發 OpenGL ES 應用時,類 GLSurfaceView 已經為我們提供了對 Display , Surface , Context 的管理,即 GLSurfaceView 内部實作了對 EGL 的封裝,可以很友善地利用接口 GLSurfaceView.Renderer 的實作,使用 OpenGL ES API 進行渲染繪制,很大程度上提升了 OpenGLES 開發的便利性。EGL 的應用

什麼是 EGL

EGL 是 OpenGL ES 和本地視窗系統(Native Window System)之間的通信接口,它的主要作用:

  • 與裝置的原生視窗系統通信;
  • 查詢繪圖表面的可用類型和配置;
  • 建立繪圖表面;
  • 在OpenGL ES 和其他圖形渲染API之間同步渲染;
  • 管理紋理貼圖等渲染資源。

OpenGL ES 的平台無關性正是借助 EGL 實作的,EGL 屏蔽了不同平台的差異(Apple 提供了自己的 EGL API 的 iOS 實作,自稱 EAGL)。

本地視窗相關的 API 提供了通路本地視窗系統的接口,而 EGL 可以建立渲染表面 EGLSurface ,同時提供了圖形渲染上下文 EGLContext,用來進行狀态管理,接下來 OpenGL ES 就可以在這個渲染表面上繪制。

NDK OpenGL ES 3.0 開發(六):EGL什麼是 EGL在 Android 平台上開發 OpenGL ES 應用時,類 GLSurfaceView 已經為我們提供了對 Display , Surface , Context 的管理,即 GLSurfaceView 内部實作了對 EGL 的封裝,可以很友善地利用接口 GLSurfaceView.Renderer 的實作,使用 OpenGL ES API 進行渲染繪制,很大程度上提升了 OpenGLES 開發的便利性。EGL 的應用

圖檔中:

  • Display(EGLDisplay) 是對實際顯示裝置的抽象;
  • Surface(EGLSurface)是對用來存儲圖像的記憶體區域 FrameBuffer 的抽象,包括 Color Buffer(顔色緩沖區), Stencil Buffer(模闆緩沖區) ,Depth Buffer(深度緩沖區);
  • Context (EGLContext) 存儲 OpenGL ES 繪圖的一些狀态資訊;

在 Android 平台上開發 OpenGL ES 應用時,類 GLSurfaceView 已經為我們提供了對 Display , Surface , Context 的管理,即 GLSurfaceView 内部實作了對 EGL 的封裝,可以很友善地利用接口 GLSurfaceView.Renderer 的實作,使用 OpenGL ES API 進行渲染繪制,很大程度上提升了 OpenGLES 開發的便利性。

當然我們也可以自己實作對 EGL 的封裝,本文就是在 Native 層對 EGL 進行封裝,不借助于 GLSurfaceView ,實作圖檔背景渲染,利用 GPU 完成對圖像的高效處理。

EGL 的應用

使用 EGL 渲染的一般步驟:

  • 擷取 EGLDisplay 對象,建立與本地視窗系統的連接配接

調用 eglGetDisplay 方法得到 EGLDisplay。

  • 初始化 EGL 方法

打開連接配接之後,調用 eglInitialize 方法初始化。

  • 擷取 EGLConfig 對象,确定渲染表面的配置資訊

調用 eglChooseConfig 方法得到 EGLConfig。

建立渲染表面 EGLSurface

通過 EGLDisplay 和 EGLConfig ,調用 eglCreateWindowSurface 或 eglCreatePbufferSurface 方法建立渲染表面,得到 EGLSurface,其中 eglCreateWindowSurface 用于建立螢幕上渲染區域,eglCreatePbufferSurface 用于建立螢幕外渲染區域。

  • 建立渲染上下文 EGLContext

通過 EGLDisplay 和 EGLConfig ,調用 eglCreateContext 方法建立渲染上下文,得到 EGLContext。

  • 綁定上下文

通過 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者綁定,綁定成功之後 OpenGLES 環境就建立好了,接下來便可以進行渲染。

  • 交換緩沖

OpenGLES 繪制結束後,使用 eglSwapBuffers 方法交換前後緩沖,将繪制内容顯示到螢幕上,而螢幕外的渲染不需要調用此方法。

  • 釋放 EGL 環境

繪制結束後,不再需要使用 EGL 時,需要取消 eglMakeCurrent 的綁定,銷毀 EGLDisplay、EGLSurface、EGLContext 三個對象。

代碼實作:

// 建立 GLES 環境
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. 擷取 EGLDisplay 對象,建立與本地視窗系統的連接配接
        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. 初始化 EGL 方法
        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. 擷取 EGLConfig 對象,确定渲染表面的配置資訊
        if(!eglChooseConfig(m_eglDisplay, confAttr, &m_eglConf, 1, &numConfigs))
        {
            LOGCATE("BgRender::CreateGlesEnv some config is wrong");
            resultCode = -1;
            break;
        }

        //4. 建立渲染表面 EGLSurface, 使用 eglCreatePbufferSurface 建立螢幕外渲染區域
        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. 建立渲染上下文 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. 綁定上下文
        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;
}

//渲染
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. 渲染
    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);

    //一旦解綁 FBO 後面就不能調用 readPixels
    //glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);

}

//釋放 GLES 環境
void BgRender::DestroyGlesEnv()
{
    //8. 釋放 EGL 環境
    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;

}           

Java 層的代碼,主要是一個 ImageView 用于展示渲染前後的圖像。

// 建立渲染對象
NativeBgRender mBgRender = new NativeBgRender();
// 初始化建立 GLES 環境
mBgRender.native_BgRenderInit();
// 加載圖檔資料到紋理
loadRGBAImage(R.drawable.java, mBgRender);
// 離屏渲染
mBgRender.native_BgRenderDraw();
// 從緩沖區讀出渲染後的圖像資料,加載到 ImageView
mImageView.setImageBitmap(createBitmapFromGLSurface(0, 0, 421, 586));
// 釋放 GLES 環境
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);
}           

聯系與交流

技術交流、擷取源碼可以掃碼添加我的微信:Byte-Flow ,領取視訊教程

「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
NDK OpenGL ES 3.0 開發(六):EGL什麼是 EGL在 Android 平台上開發 OpenGL ES 應用時,類 GLSurfaceView 已經為我們提供了對 Display , Surface , Context 的管理,即 GLSurfaceView 内部實作了對 EGL 的封裝,可以很友善地利用接口 GLSurfaceView.Renderer 的實作,使用 OpenGL ES API 進行渲染繪制,很大程度上提升了 OpenGLES 開發的便利性。EGL 的應用

繼續閱讀