Opengl中EGL的常见用法

综述:

在opengl的使用场景中我们经常遇到需要使用opengl的环境进行数据处理,比如在对Opengl进行视频帧数据处理之后需要继续对视频数据编码,比如在一些场景我们需要对视频帧处理,但是并不需要进行上屏显示等处理,比如我们在对一些视频在播放时需要再解码完成之后对视频进行特效处理等。此三种场景无论我们是否已经有了opengl的环境,都需要我们在进行业务时,基于opengl的环境,也就是Egl环境下才能进行处理,有些场景我们能借助当前的opengl环境进行处理,我们需要创建EGL环境的时候 共享当前的opengl环境,即将当前egl的context传递出去,有些场景没有opengl的环境,需要我们自己单独创建EGL环境。EGL环境时使用opengl必须创建的,在Android 中我们使用GlSurfaceView时,就是系统封装好了EGL环境,因此我们在自己使用EGL环境的时候,可以仿照系统进行创建,然后进行管理

1:EGL是什么

EGL是什么,简单来说,opengl是一个跨平台的图形绘制API,opengl可以在不同的操作系统上实现图形绘制,使用同一套API,但是针对不同的平台之间的差异带来的绘制差异如何解决?这个时候由相关组织就抽象了一层,就是EGL,在不同的平台上实现opengl环境绘制之前,都需要实现EGL环境,根据不同平台配置好EGL环境之后,我们使用opengl来绘制就可以跨平台了

EGL_api.png

上面这张经典的图可以说明 EGL在使用opengl绘制时所处的位置,在Android中使用opengl,由系统实现了一套 EGL的环境,可以参考GlSurfaceView的源码

2:EGL怎么用

EGL怎么使用?请参考下图:

EGL_抽象.png

EGL总共管理的3个大类型的抽象类,一个是 EGLDisplay ,一个是EGLContext,还有一个是EGLSurface,EglContext还可以提供给到其他线程,则不同的线程之间可以共享EGL的环境,下面介绍下一般的创建过程

   public BaseEglCore(EGLContext sharedContext, int flags) {
        throwableForStackTrace = new Throwable();

        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("EGL already set up");
        }


        if (sharedContext == null) {
            sharedContext = EGL14.EGL_NO_CONTEXT;
        }
        
        //创建EGLDisplay
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("unable to get EGL14 display");
        }
        //初始化EGLDisplay
        int[] version = new int[2];
        if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
            mEGLDisplay = null;
            throw new RuntimeException("unable to initialize EGL14");
        }
      
      
        // Try to get a GLES3 context, if requested.
        if ((flags & FLAG_TRY_GLES3) != 0) {
            //Log.d(TAG, "Trying GLES 3");
            //配置opengl的环境
            EGLConfig config = getConfig(flags, 3);
            if (config != null) {
                int[] attrib3List = {
                        EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
                        EGL14.EGL_NONE
                };
                //创建EGLcontext
                EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
                        attrib3List, 0);

                if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
                    //Log.d(TAG, "Got GLES 3 config");
                    mEGLConfig = config;
                    mEGLContext = context;
                    mGlVersion = 3;
                }
            }
        }

        // Confirm with query.
        int[] values = new int[1];
        EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
                values, 0);
        Log.d(TAG, "EGLContext created, client version " + values[0]);
    }
    
      private EGLConfig getConfig(int flags, int version) {
        int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
        if (version >= 3) {
            renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
        }

        // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
        // doesn't really help.  It can also lead to a huge performance hit on glReadPixels()
        // when reading into a GL_RGBA buffer.
        int[] attribList = {
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_ALPHA_SIZE, 8,
                //EGL14.EGL_DEPTH_SIZE, 16,
                //EGL14.EGL_STENCIL_SIZE, 8,
                EGL14.EGL_RENDERABLE_TYPE, renderableType,
                EGL14.EGL_NONE, 0,      // placeholder for recordable [@-3]
                EGL14.EGL_NONE
        };
        if ((flags & FLAG_RECORDABLE) != 0) {
            attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
            attribList[attribList.length - 2] = 1;
        }
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs = new int[1];
        if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
                numConfigs, 0)) {
            Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
            return null;
        }
        return configs[0];
    }

创建好了 EglCore之后,我们就已经创建成功了 EglDisplay,EglContext,这个时候我们的普通线程已经成为了GLthread线程,但是我们的EGL环境尚未创建完成,正如前面所说,EGL需要的三个抽象实体还未全部创建出来,想要在当前的线程中使用opengl API,还需要创建EglSurface,创建过程如下:

//创建一个离屏的egl Surface    
 public OffscreenSurface(BaseEglCore eglCore, int width, int height) {
        super(eglCore);
        createOffscreenSurface(width, height);
}
public void createOffscreenSurface(int width, int height) {
        if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
            throw new IllegalStateException("surface already created");
        }
        mEGLSurface = mEglCore.createOffscreenSurface(width, height);
        mWidth = width;
        mHeight = height;
}


/**
     * Associates an EGL surface with the native window surface.
     * <p>
     * Set releaseSurface to true if you want the Surface to be released when release() is
     * called.  This is convenient, but can interfere with framework classes that expect to
     * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the
     * surfaceDestroyed() callback won't fire).
     */
//创建一个基于屏幕上egl Surface的
    public WindowSurface(BaseEglCore eglCore, Surface surface, boolean releaseSurface) {
        super(eglCore);
        createWindowSurface(surface);
        mSurface = surface;
        mReleaseSurface = releaseSurface;
    }
/**
     * Creates a window surface.
     * <p>
     * @param surface May be a Surface or SurfaceTexture.
     */
    public void createWindowSurface(Object surface) {
        if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
            throw new IllegalStateException("surface already created");
        }
        mEGLSurface = mEglCore.createWindowSurface(surface);

        // Don't cache width/height here, because the size of the underlying surface can change
        // out from under us (see e.g. HardwareScalerActivity).
        //mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
        //mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
    }

到此我们就把EGL的一个简单环境搭建起来了

3:EGL中常见使用场景

Egl在Android或者其他平台上,是使用Opengl的前置条件,需要自行配置。Android中一般如果要是在在屏幕上最终要显示出来图像可以使用Android中配置好的GlSurfaceView这个API,这个API已经由Android系统进行配置了一个EGL环境,使用者可以直接设置Render之后,在onDraw中进行绘制。

在一些场景中,我们可能不需要将图像最终显示在屏幕上,但是需要使用Opengl来对图像进行处理,典型几个应用场景如下:

3.1:在使用opengl进行多图层处理时,我们到最终显示前需要多次对纹理进行处理,此时如果我们没有opengl的环境,需要自行搭建,此种业务为我们需要对图像数据进行AI处理,需要将纹理作为参数喂给我们的AI的SDK,或者我们需要使用opengl对图像进行旋转,缩放,裁剪,多图层合并等2种常见业务场景。

3.2:在使用oepngl进行图像渲染上屏渲染时,我们在上了屏幕显示的同时需要对显示的数据进行录制,此时如何将之前在纹理上的数据进行录制?需要我们共享屏幕上显示的opengl环境的Eglcontext 到MediaCodec,让MediaCodec录制的时候采集的数据是纹理上的数据。主要常见的业务场景是推流等

3.3:在显示数据到屏幕上时,我们显示的数据是从视频文件中获取的,需要我们自己搭建Egl环境,即搭建Egl环境创建纹理穿件Surface给到MediaCodec解码,MediaCodec解码出来的数据直接给到刚才创建的OES纹理上,进行绘制。常见的使用场景播放器等

综上,使用Egl环境常见的几个场景,即如果不上屏幕显示,需要自行搭建EGL环境。其次是如果涉及到编码和解码,都需要自行创建EGL的环境或者共享已有的EGL环境来实现业务场景

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容