天天看點

EGL 作用及其使用

大家好,接下來将為大家介紹EGL 作用及其使用。

1、什麼是EGL

EGL 是 OpenGL ES 渲染 API 和本地視窗系統(native platform window system)之間的一個中間接口層,EGL作為OpenGL ES與顯示裝置的橋梁,讓OpenGL ES繪制的内容能夠在呈現目前裝置上。它主要由系統制造商實作。

EGL具有如下作用:

a:與裝置的原生視窗系統通信。

b:查詢繪圖表面的可用類型和配置。

c:建立繪圖表面。

d:在OpenGL ES 和其他圖形渲染API之間同步渲染。

e:管理紋理貼圖等渲染資源。

2、EGL和OpenGL ES的關系

從上面的講解我們基本上可以知道,EGL 為OpenGL提供繪制表面。或者說EGL是OpenGl ES的渲染畫布。

EGL作為OpenGL ES與顯示裝置的橋梁,讓OpenGL ES繪制的内容能夠在呈現目前裝置上。

EGL 作用及其使用

3、EGL繪圖的基本步驟

EGL 作用及其使用

簡單講解下各部分的作用:

a:Display(EGLDisplay) 是對實際顯示裝置的抽象。

b:Surface(EGLSurface)是對用來存儲圖像的記憶體區域FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer。

c:Context (EGLContext) 存儲 OpenGL ES繪圖的一些狀态資訊。

4.1、EGL的基本使用步驟:

a:擷取 EGL Display 對象:通過eglGetDisplay()方法來傳回EGLDisplay作為OpenGL ES的渲染目标。

if ( (mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) == EGL14.EGL_NO_DISPLAY) {
                throw new RuntimeException("unable to get EGL14 display"); }           

複制

b:初始化與 EGLDisplay 之間的連接配接:eglInitialize()。第一參數代表Major版本,第二個代表Minor版本。如果不關心版本号,傳0或者null就可以了。

if (!EGL14.eglInitialize(mEGLDisplay, 0, 0)) {
     throw new RuntimeException("unable to initialize EGL14"); }           

複制

c:擷取 EGLConfig 對象:eglChooseConfig()。

int[] attribList = {
                    EGL14.EGL_RED_SIZE, 8,
                    EGL14.EGL_GREEN_SIZE, 8,
                    EGL14.EGL_BLUE_SIZE, 8,
                    EGL14.EGL_ALPHA_SIZE, 8,
                    EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES3_BIT,
                    EGL_RECORDABLE_ANDROID, 1,
                    EGL14.EGL_NONE
            };
            EGLConfig[] configs = new EGLConfig[1];
            int[] numConfigs = new int[1];
            EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
                    numConfigs, 0);           

複制

d:建立 EGLContext 執行個體:接下來我們需要建立OpenGL的上下文環境 EGLContext 執行個體,這裡值得留意的是,OpenGL的任何一條指令都是必須在自己的OpenGL上下文環境中運作,我們可以通過eglCreateContext()方法來建構上下文環境:

int[] attrib_list = {
                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                    EGL14.EGL_NONE
            };
            mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
                    attrib_list, 0);           

複制

e:建立 EGLSurface 執行個體:通過eglCreateWindowSurface()方法建立一個實際可以顯示的EGLSurface。

private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
      int[] surfaceAttribs = {
                    EGL14.EGL_NONE
            };
            mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
                    surfaceAttribs, 0);           

複制

f:連接配接 EGLContext 和 EGLSurface:通過上面的步驟,EGL的準備工作做好了,一方面我們為OpenGL ES渲染提供了目标及上下文環境EGLContext,可以接收到OpenGl ES渲染出來的紋理;另一方面我們連接配接好了裝置顯示屏EGLSurface(這裡指SurfaceView或者TextureView),接下來我們講解如何在建立好的EGL環境下工作的。

首先我們有一點必須要明确,OpenGL ES 的渲染必須新開一個線程,并為該線程綁定顯示裝置及上下文環境(EGLContext)。

前面有說過OpenGL指令必須要在其上下文環境中才能執行。是以我們首先要通過 eglMakeCurrent()方法來綁定該線程的顯示裝置及上下文。

EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);           

複制

g:當我們綁定完成之後,我們就可以進行RenderLoop循環了,使用 OpenGL ES API 繪制圖形:gl_*() 。

h:繪制好之後,切換 front buffer 和 back buffer 重新整理顯示:eglSwapBuffer()。

EGL的工作模式是雙緩沖模式,其内部有兩個FrameBuffer(幀緩沖區,可以了解為一個圖像存儲區域),當EGL将一個FrameBuffer顯示到螢幕上的時候,另一個FrameBuffer就在背景等待OpenGL ES進行渲染輸出。

直到調用了eglSwapBuffer()這條指令的時候,才會把前台的FrameBuffers和背景的FrameBuffer進行交換,這時界面呈現的就是OpenGL ES剛剛渲染的内容了。

核心:雙緩沖(Double Buffer)

應用程式使用單緩沖繪圖時可能會存在圖像閃爍的問題。這是因為生成的圖像不是一下子被繪制出來的,而是按照從左到右,由上而下逐像素地繪制而成的。

最終圖像不是在瞬間顯示給使用者,而是通過一步一步生成的,這會導緻渲染的結果很不真實。為了規避這些問題,我們應用雙緩沖渲染視窗應用程式。前緩沖儲存着最終輸出的圖像,它會在螢幕上顯示;而所有的的渲染指令都會在後緩沖上繪制。

當所有的渲染指令執行完畢後,我們交換(Swap)前緩沖和後緩沖,這樣圖像就立即呈顯出來,之前提到的不真實感就消除了。

EGL14.eglSwapBuffer();           

複制

i:斷開并釋放與 EGLSurface 關聯的 EGLContext 對象:eglRelease()。

j:删除 EGLSurface 對象,eglDestroySurface()。

EGL14.eglDestroySurface(display, surface);           

複制

k:删除 EGLContext 對象,

EGL14.eglDestroyContext(display, context);           

複制

l:終止與 EGLDisplay 之間的連接配接,

EGL14.eglTerminate(display);           

複制

5、EGL 環境搭建示例

import android.icu.text.UFormat;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceView;
 
import javax.microedition.khronos.egl.EGL;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
 
import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
 
 
public class EGLHelper {
    private EGL10 mEgl;
    private EGLDisplay mEglDisplay;
    private EGLContext mEglContext;
    private EGLSurface eglSurface;
 
    public void init(Surface surface, EGLContext eglContext){
        //1.得到Egl執行個體
        mEgl = (EGL10) EGLContext.getEGL();
        //2.得到預設的顯示裝置(就是視窗)
        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        //判斷擷取預設顯示裝置是否成功
        if (mEglDisplay==EGL10.EGL_NO_DISPLAY){
            throw new RuntimeException("eglGetDisplay failed");
        }
        //3.初始化預設顯示裝置(初始化EGL)
        //主版本号和次版本号
       int[] version=new int[2];
        boolean initialize = mEgl.eglInitialize(mEglDisplay, version);
        if (!initialize){
            throw new RuntimeException("eglInitialize failed");
        }
        Log.e("ike","version:"+version[0]);
        Log.e("ike","version:"+version[1]);
 
        //4.設定顯示裝置的屬性
        int[] attribute=new int[] {
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 8,
                EGL10.EGL_STENCIL_SIZE, 4,
                EGL10.EGL_NONE};
        //根據屬性資訊從系統的所有的配置資訊中,擷取支援該屬性清單的配置資訊的個數。一般來說就選取一個就好了
        int[] num_config = new int[1];
        boolean chooseConfig = mEgl.eglChooseConfig(mEglDisplay, attribute, null, 1, num_config);
        if (!chooseConfig){
            throw new RuntimeException("eglChooseConfig failed");
 
        }
        //判斷是否選擇到符合傳入參數的配置資訊
        if (num_config[0]<=0){
            throw new IllegalArgumentException(
                    "No configs match configSpec");
        }
        //5.從系統中擷取對應屬性的配置
        EGLConfig[] eglConfigs=new EGLConfig[num_config[0]];
        boolean eglChooseConfig = mEgl.eglChooseConfig(mEglDisplay, attribute, eglConfigs, num_config[0], num_config);
        if (!eglChooseConfig){
            throw new RuntimeException("eglChooseConfig$2 failed");
        }
        //6. 建立EglContext
        //如果eglContext==null則建立新的egl上下文
        int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3,
            EGL10.EGL_NONE };
        if (eglContext==null){
            mEglContext = mEgl.eglCreateContext(mEglDisplay, eglConfigs[0], EGL_NO_CONTEXT, null);
 
        }else {
            //根據傳入eglContext建立可以共享的egl上下文
            mEglContext = mEgl.eglCreateContext(mEglDisplay, eglConfigs[0], eglContext, null);
        }
        if (mEglContext==null||mEglContext== EGL_NO_CONTEXT){
            mEglContext=null;
            throw new RuntimeException("eglCreateContext failed");
        }
        //7.建立surface
        eglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, eglConfigs[0], surface, null);
        if (eglSurface==null||eglSurface==EGL10.EGL_NO_SURFACE){
            eglSurface=null;
            throw new RuntimeException("eglCreateWindowSurface failed");
        }
        //8.綁定EGLContext和surface到顯示裝置
        boolean makeCurrent = mEgl.eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext);
        if (!makeCurrent){
            Log.e("ike","eglMakeCurrent failed");
        }
    }
 
    /**
     * 重新整理資料
     */
    public void swapBuffers(){
        if (mEgl!=null){
            boolean eglSwapBuffers = mEgl.eglSwapBuffers(mEglDisplay, eglSurface);
            if (!eglSwapBuffers){
                Log.e("ike","eglSwapBuffers failed");
 
            }
        }
    }
 
    /**
     * 擷取EGL上下文
     * @return
     */
    public EGLContext getEGLCOntext(){
        return mEglContext;
    }
    public void destoryEgl(){
       if (mEgl!=null){
           //與顯示裝置解綁,銷毀eglsurface
           mEgl.eglMakeCurrent(mEglDisplay,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE,EGL_NO_CONTEXT);
           mEgl.eglDestroySurface(mEglDisplay,eglSurface);
           eglSurface=null;
           //銷毀上下文
           mEgl.eglDestroyContext(mEglDisplay,mEglContext);
           mEglContext=null;
           //銷毀顯示裝置
           mEgl.eglTerminate(mEglDisplay);
           mEglDisplay=null;
           mEgl=null;
       }
    }
}           

複制

版權聲明:本文為CSDN部落客「美顔特效.音視訊」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/u010281924/article/details/105296617

-- END --