Desarrollo Android OpenGL - EGL detallado

prefacio

Si desea utilizar OpenGl para personalizar la cámara, aún debe comprender EGL.

Probablemente la mayoría de los desarrolladores han usado OpengGL pero no saben qué es EGL. ¿Cuál es el papel de EGL? En realidad, esto no es sorprendente en absoluto, porque GlSurfaceView en Android ya configuró el entorno EGL, lo ha estado usando, pero simplemente no sabe su existencia.

Mucha gente puede tener una pregunta al usar OpenGl ES para renderizar datos, ¿a dónde van los datos renderizados? Sin ver el lienzo, ¿todas las vistas personalizadas en Android no tienen lienzos?

Este artículo sirve para solucionar esta serie de problemas. Por supuesto, si está interesado en el desarrollo de codificación de video, audio y cámara personalizada, puede obtener información sobre mi proyecto de código abierto AndroidCamera , que incluye pegatinas de reconocimiento facial de video, belleza, grabación segmentada, recorte de video, procesamiento de cuadros de video y adquisición de clave de video. Marco, rotación de video, agregar filtros, agregar marcas de agua, sintetizar Gif a video y muchas otras funciones, pueden aprender y comunicarse juntos.

Si no sabes mucho sobre OpenGL, echa un vistazo al artículo anterior:

Tabla de contenido

A través de este artículo puedes aprender lo siguiente:

  1. ¿Qué es EGL?.
  2. La relación entre EGL y OpenGL ES
  3. Pasos básicos para dibujar con EGL.
  4. Análisis de EGL en el código fuente de GlSurfaceView
  5. Resumir

1. ¿Entiendes qué es EGL?

¿Qué es EGL? EGL es la interfaz entre las API de representación (por ejemplo, OpenGL, OpenGL ES, OpenVG) y el sistema de ventanas nativo. Maneja la gestión del contexto de gráficos, la creación de superficie/búfer, la vinculación y la sincronización de renderizado, y utiliza otras API de Khronos para renderizado 2D y 3D de modo mixto, acelerado y de alto rendimiento API de cliente de renderizado OpenGL/OpenGL ES Sistema de ventana de plataforma nativa de API de cliente de renderizado OpenVG. Un poco de comprensión de esto está bien, solo necesita saber que es una interfaz utilizada para proporcionar una interfaz de dibujo para OpenGl ES.

El papel de EGL:

  1. Comuníquese con el sistema de ventanas nativo del dispositivo.
  2. Consulta de tipos y configuraciones disponibles de superficies de dibujo.
  3. Crear una superficie de dibujo.
  4. 在OpenGL ES 和其他图形渲染API之间同步渲染。
  5. 管理纹理贴图等渲染资源。

这里关于EGL的介绍就讲这么多,如果你有兴趣的话你可以继续到这里去see yi seeUnderstanding Android EGL

2. EGL和OpenGl ES的关系

从上面的讲解我们基本上可以知道,EGL是为OpenGl提供绘制表面的。

对的,这就是OpenGl ES数据渲染画布所在了。想必到这里大家也清楚了渲染数据去处的问题了。

EGL还有什么用呢?EGL可以理解为OpenGl ES ES和设备之间的桥梁。完全可以这么理解。

3. EGL绘图的基本步骤

imagen.png 先上图,再说话。

简单讲解下各部分的作用:

  1. Display(EGLDisplay) 是对实际显示设备的抽象。
  2. Surface(EGLSurface)是对用来存储图像的内存区FrameBuffer 的抽象,包括Color Buffer,Stencil Buffer,Depth Buffer。
  3. Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息。

EGL的基本使用步骤:

  1. 首先我们需要知道绘制内容的目标在哪里,EGLDisplayer是一个封装系统屏幕的数据类型,通常通过eglGetDisplay方法来返回EGLDisplay作为OpenGl ES的渲染目标,eglGetDisplay()
 if ( (mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) == EGL14.EGL_NO_DISPLAY) {
                throw new RuntimeException("unable to get EGL14 display");
            }
复制代码
  1. 初始化显示设备,第一参数代表Major版本,第二个代表Minor版本。如果不关心版本号,传0或者null就可以了。初始化与 EGLDisplay 之间的连接:eglInitialize()
            if (!EGL14.eglInitialize(mEGLDisplay, 0, 0)) {
                throw new RuntimeException("unable to initialize EGL14");
            }
复制代码
  1. 下面我们进行配置选项,使用eglChooseConfig()方法,Android平台的配置代码如下:
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_ES2_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);
复制代码
  1. 接下来我们需要创建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);
复制代码

eglCreateContext中的第三个参数可以传入一个EGLContext类型的变量,改变量的意义是可以与正在创建的上下文环境共享OpenGl资源,包括纹理ID,FrameBuffer以及其他Buffer资源。如果没有的话可以填写Null.

  1. 通过上面四步,获取OpenGl 上下文之后,说明EGL和OpenGl ES端的环境已经搭建完毕,也就是说OpengGl的输出我们可以获取到了。下面的步骤我们讲如何将EGl和设备屏幕连接起来。如果连接呢?当然,这时候我们就要使用EGLSurface了,我们通过EGL库提供eglCreateWindowSurface可以创建一个实际可以显示的surface.当然,如果需要离线的surface,我们可以通过eglCreatePbufferSurface创建。eglCreateWindowSurface()
     private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
      int[] surfaceAttribs = {
                    EGL14.EGL_NONE
            };
            mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
                    surfaceAttribs, 0);
复制代码
  1. 通过上面的步骤,EGL的准备工作做好了,一方面我们为OpenGl ES渲染提供了目标及上下文环境,可以接收到OpenGl ES渲染出来的纹理,另一方面我们连接好了设备显示屏(这里指SurfaceView或者TextureView),接下来我们讲解如何在创建好的EGL环境下工作的。首先我们有一点必须要明确,OpenGl ES 的渲染必须新开一个线程,并为该线程绑定显示设备及上下文环境(Context)。因为前面有说过OpenGl指令必须要在其上下文环境中才能执行。所以我们首先要通过 eglMakeCurrent()方法来绑定该线程的显示设备及上下文。
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
复制代码
  1. 当我们绑定完成之后,我们就可以进行RenderLoop循环了。这里简单说一下,EGL的工作模式是双缓冲模式,其内部有两个FrameBuffer(帧缓冲区,可以理解为一个图像存储区域),当EGL将一个FrameBuffer显示到屏幕上的时候,另一个FrameBuffer就在后台等待OpenGl ES进行渲染输出。知道调用了eglSwapBuffers这条指令的时候,才会把前台的FrameBuffers和后台的FrameBuffer进行交换,这样界面呈现的就是OpenGl ES刚刚渲染的结构了。
mInputSurface.swapBuffers();
复制代码
  1. 当然,在所有的操作都执行完之后,我们要销毁资源。特别注意,销毁资源必须在当前线程中进行,不然会报错滴。首先我们销毁显示设备(EGLSurface),然后销毁上下文(EGLContext),停止并释放线程,最后终止与EGLDisplay之间的链接,
 EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
                EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
                EGL14.eglReleaseThread();
                EGL14.eglTerminate(mEGLDisplay);
复制代码

4. GlSurfaceView源码中分析EGL

上面我们有提到过Android GlSurfaceView中已经帮忙配置好了EGL,下面我们来看下EGL在GLSurfaceView中的具体实现过程:我们平时使用GlSurfaceView怎么使用的呢?xml中布置,然后setRenderer(this),然后调用下setRenderMode()就OK了。特比简单。但是GlSurfaceView内部原理是什么呢?我们现在来一探究竟:

特别声明:下面所有代码皆为GlSurfaView中的源码。

  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;//为当前的View设置一个渲染器
        mGLThread = new GLThread(mThisWeakRef);
        //创建线程并开启
        mGLThread.start();
    }
复制代码

setRenderer函数主要干了2件事,一个是给View设置一个渲染器对象二是创建并开启渲染线程。线程start后执行guardedRun,run函数一个while(true)循环,渲染数据。

 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;//为当前的View设置一个渲染器
        mGLThread = new GLThread(mThisWeakRef);
        //创建线程并开启
        mGLThread.start();
    }
复制代码

到这里为止,是不是感觉特别熟悉?我们已经看到了onSurfaceCreated(),onSurfaceChanged(),onDrawFrame()的回调了。在这些方法之前有个EglHelper类的创建,接下来进入EGL的配置环节。请系好安全带:

public void start(){
		……
	 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);//1. 获取 EGL Display 对象
		……
		//2. 初始化与 EGLDisplay 之间的连接
		 if(!mEgl.eglInitialize(mEglDisplay, version)) {
                throw new RuntimeException("eglInitialize failed");
            }
	     ……
	       mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);//3. 获取 EGLConfig 对象
	        /*
                * Create an EGL context. We want to do this as rarely as we can, because an
                * EGL context is a somewhat heavy object.
                */
                mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);//4 创建 EGLContext 实例
               ……
 }
复制代码

Bueno, siga los pasos de dibujo de EGL anteriores y compare el código fuente de GlSurfaceView. El anciano no hablará de eso aquí. ¿No me preguntes porque?

Resumir

En resumen, después de leer lo anterior, debes tener una comprensión más clara de las preguntas mencionadas al principio, creo que ya tienes la respuesta en tu corazón. ¿Qué resumir? Hablemos de emociones, la mejor manera de aprender a programar es mirar el código fuente, no hay nada más efectivo que leer el código fuente para aprender. Mirando GlSurfaceView nuevamente hoy, tengo una comprensión diferente que antes. Finalmente, si tiene alguna pregunta sobre el aprendizaje de la cámara personalizada, puede dejarme un mensaje para comunicarnos y aprender juntos. Cuenta pública de WeChat: aserbao.

Si está aprendiendo el desarrollo de una cámara personalizada de Android, bienvenido a mi proyecto de código abierto AndroidCamera para comunicarse.

Si crees que el artículo está bien escrito, por favor dale me gusta, si crees que no está bien escrito, por favor deja un mensaje de crítica y corrección, para que pueda revisarlo y revisarlo a tiempo.

Feishu 20220713-100110.gif

Estoy participando en el reclutamiento del programa de firma de creadores de la Comunidad Tecnológica de Nuggets, haga clic en el enlace para registrarse y enviar .

Supongo que te gusta

Origin juejin.im/post/7119673311792988168
Recomendado
Clasificación