Android OpenGL-Entwicklung – EGL detailliert

Vorwort

Wenn Sie OpenGl verwenden möchten, um die Kamera anzupassen, muss EGL noch verstanden werden.

Wahrscheinlich haben die meisten Entwickler OpengGL verwendet, wissen aber nicht, was EGL ist? Welche Rolle spielt die EGL? Das ist eigentlich überhaupt nicht überraschend, denn GlSurfaceView in Android hat die EGL-Umgebung bereits konfiguriert, Sie haben sie verwendet, aber Sie wissen einfach nicht, dass es sie gibt.

Viele Leute haben möglicherweise eine Frage, wenn sie OpenGl ES zum Rendern von Daten verwenden, wo gehen die gerenderten Daten hin? Haben nicht alle benutzerdefinierten Ansichten in Android Leinwände, ohne die Leinwand zu sehen?

Dieser Artikel dient dazu, diese Reihe von Problemen zu lösen. Wenn Sie an der Entwicklung benutzerdefinierter Kameras, Audio- und Videocodierungen interessiert sind, können Sie sich natürlich über mein Open-Source-Projekt AndroidCamera informieren , das Video-Gesichtserkennungsaufkleber, Beauty, segmentierte Aufzeichnung, Videozuschnitt, Videoframe-Verarbeitung und Videoschlüsselerfassung umfasst Rahmen, Videodrehung, Hinzufügen von Filtern, Hinzufügen von Wasserzeichen, Synthetisieren von GIFs zu Videos und viele andere Funktionen, Sie können gemeinsam lernen und kommunizieren.

Wenn Sie nicht viel über OpenGL wissen, werfen Sie einen Blick auf den vorherigen Artikel:

Inhaltsverzeichnis

Durch diesen Artikel können Sie Folgendes lernen:

  1. Was ist EGL?.
  2. Die Beziehung zwischen EGL und OpenGL ES
  3. Grundlegende Schritte zum Zeichnen mit EGL.
  4. Analyse von EGL im GlSurfaceView-Quellcode
  5. Zusammenfassen

1. Verstehen Sie, was EGL ist?

Was ist EGL? EGL ist die Schnittstelle zwischen Rendering-APIs (z. B. OpenGL, OpenGL ES, OpenVG) und dem nativen Windowing-System. Es handhabt Grafikkontextverwaltung, Oberflächen-/Puffererstellung, Bindung und Renderingsynchronisierung und verwendet andere Khronos-APIs für hochleistungsfähiges, beschleunigtes 2D- und 3D-Rendering im gemischten Modus. OpenGL/OpenGL ES-Rendering-Client-API OpenVG-Rendering-Client-API natives Plattformfenstersystem. Ein wenig Verständnis dafür ist in Ordnung, Sie müssen nur wissen, dass es sich um eine Schnittstelle handelt, die verwendet wird, um eine Zeichenschnittstelle für OpenGl ES bereitzustellen.

Die Rolle der EGL:

  1. Kommunizieren Sie mit dem nativen Fenstersystem des Geräts.
  2. Fragen Sie verfügbare Typen und Konfigurationen von Zeichenoberflächen ab.
  3. Erstellen Sie eine Zeichenfläche.
  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绘图的基本步骤

Bild.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 实例
               ……
 }
复制代码

Nun, bitte folgen Sie den obigen EGL-Zeichnungsschritten und vergleichen Sie den Quellcode von GlSurfaceView. Der alte Mann wird hier nicht darüber sprechen. Frage mich nicht warum?

Zusammenfassen

Zusammenfassend lässt sich sagen, dass Sie nach dem Lesen des oben Gesagten ein klareres Verständnis für die eingangs genannten Fragen haben sollten, und ich glaube, Sie haben die Antwort bereits in Ihrem Herzen. Was soll man zusammenfassen? Reden wir über Emotionen. Programmieren lernt man am besten, wenn man sich den Quellcode anschaut. Es gibt nichts Effektiveres, als den Quellcode zu lesen, um zu lernen. Wenn ich mir GlSurfaceView heute noch einmal anschaue, habe ich ein anderes Verständnis als früher. Wenn Sie Fragen zum Erlernen der benutzerdefinierten Kamera haben, können Sie mir schließlich eine Nachricht hinterlassen, um gemeinsam zu kommunizieren und zu lernen. Öffentliches WeChat-Konto: aserbao.

Wenn Sie die Entwicklung einer benutzerdefinierten Android-Kamera lernen, herzlich willkommen bei meinem Open-Source-Projekt AndroidCamera zur Kommunikation.

Wenn Sie den Artikel gut geschrieben finden, geben Sie ihm bitte einen Daumen nach oben. Wenn Sie ihn nicht gut geschrieben finden, hinterlassen Sie bitte eine Nachricht für Kritik und Korrektur, damit ich ihn rechtzeitig überprüfen und überarbeiten kann.

Feishu 20220713-100110.gif

Ich nehme an der Rekrutierung des Signierprogramms für Ersteller der Nuggets Technology Community teil. Klicken Sie auf den Link, um sich zu registrieren und einzureichen .

Ich denke du magst

Origin juejin.im/post/7119673311792988168
Empfohlen
Rangfolge