Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL

OpenGL 学习教程
Android OpenGL ES 学习(一) – 基本概念
Android OpenGL ES 学习(二) – 图形渲染管线和GLSL
Android OpenGL ES 学习(三) – 绘制平面图形
Android OpenGL ES 学习(四) – 正交投影
Android OpenGL ES 学习(五) – 渐变色
Android OpenGL ES 学习(六) – 使用 VBO、VAO 和 EBO/IBO 优化程序
Android OpenGL ES 学习(七) – 纹理
Android OpenGL ES 学习(八) –矩阵变换
Android OpenGL ES 学习(九) – 坐标系统和。实现3D效果
Android OpenGL ES 学习(十) – GLSurfaceView 源码解析GL线程以及自定义 EGL
代码工程地址: https://github.com/LillteZheng/OpenGLDemo.git

前面我们已经学习了 GL 的基本实现效果,我们一般的操作顺序就是

  • onSurfaceCreated: 进行着色器的加载编译
  • onSurfaceChanged:设置 gl 的大小,或者设置正交投影
  • onDrawFrame:绘制

那你会不会好奇,为啥我们按照这样的顺序,弄完就可以正常显示了呢?你是否对OpenGL 绘制的 “完整流程” 感兴趣呢?你是否也遇到下面这些疑问呢?

  1. GL线程和普通线程有什么区别?
  2. texture所占用的空间是跟GL线程绑定的吗?
  3. 为什么通常一个GL线程的texture等数据,在另一个GL线程没法用?
  4. 为什么通常GL线程销毁后,为什么texture也跟着销毁了?
  5. 不同线程如何共享OpenGL数据

这一章,我们带着这些疑问,来探究 OpenGL 渲染的完整流程,今天要完成的效果,不使用GLSurfaceView,编写 EGL + SurfaceVIew 实现 3D 效果:
在这里插入图片描述

一. 渲染完整流程

为什么叫完整流程?前面也说道,Android 系统把复杂的过程都封装好了,我们只需要调用 GlSurfaceView 就可以很轻松的拿到OpenGL 的渲染环境,然后编写着色器代码和逻辑即可。
但实际上,OpenGL 的核心流程应该是这样的:

在这里插入图片描述
为了得到真相,我们通过查看GLSurfaceView 的源码去看。

二. GLSurfaceView 源码

从入口出发,我们都是调用 glSurface.setRenderer() 去设置的,进到源码后可以发现,它主要做了以下几件事情:

    // EGLConfig 对象.用于指定OpenGL颜色、深度、模版等设置
    if (mEGLConfigChooser == null) {
        mEGLConfigChooser = new SimpleEGLConfigChooser(true);
    }
<span class="token comment">// EGLContext 对象.用于提供EGLContext创建和销毁的处理</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>mEGLContextFactory <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    mEGLContextFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultContextFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// EGLSurface窗口对象. 用于提供EGLSurface创建和销毁的处理</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>mEGLWindowSurfaceFactory <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    mEGLWindowSurfaceFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultWindowSurfaceFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
 
<span class="token comment">//开启 GLThread线程 </span>
mRenderer <span class="token operator">=</span> renderer<span class="token punctuation">;</span>
mGLThread <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GLThread</span><span class="token punctuation">(</span>mThisWeakRef<span class="token punctuation">)</span><span class="token punctuation">;</span>
mGLThread<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

额,看完这个注释,你可能会一头雾水,EGL 是啥,EGLSurface 又是啥,看来我们需要先了解 EGL .

2.1 EGL

EGL 是 OpenGL 与本地窗口系统 Native Window System)之间的通信接口,它的主要作用是:

  • 与设备的原生窗口系统通信
  • 查询绘图表面的可用类型和配置
  • OpenGL 和其他图形渲染 API 渲染

不同平台上EGL配置是不一样的,而OpenGL的调用方式是一致的,也就是说OpenGL的跨平台特性依赖于EGL接口。

EGL 通过创建 eglSurface,和上下文 eglContext,就可以通过相关 api 访问本地窗口系统,实现绘制。如下图,体现了 EGL ,display 和 context 三者之间的关系。

在这里插入图片描述

  • Display(EglDisplay):实际显示设备的抽象,你可以理解成显示模块
  • Surface(EglSurface):用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer (颜色缓冲区),Stencil Buffer(模板缓冲区),Depth Buffer(深度缓冲区)
  • Context(EglContext):存储 OpenGL ES 绘图的一些状态信息,比如存储 texture

在开发时,GLSurfaceView 已经帮我们对 Display,Surface,Context 进行封装管理,我们可以很方便的调用 GLSurfaceView.Render ,实现渲染绘制。
使用 EGL 渲染的一般步骤:

  1. 获取 EGLDisplay 对象,建立与本地窗口系统的连接:调用 eglGetDisplay 方法得到 EGLDisplay。
  2. 初始化 EGL 方法:打开连接之后,调用 eglInitialize 方法初始化。
  3. 获取 EGLConfig 对象,确定渲染表面的配置信息:调用 eglChooseConfig 方法得到 EGLConfig。
  4. 创建渲染表面 EGLSurface:通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 或 eglCreatePbufferSurface 方法创建渲染表面,得到 EGLSurface,其中 eglCreateWindowSurface 用于创建屏幕上渲染区域,eglCreatePbufferSurface 用于创建屏幕外渲染区域。
  5. 创建渲染上下文 EGLContext :通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext。
  6. 绑定上下文:通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,绑定成功之后 OpenGLES 环境就创建好了,接下来便可以进行渲染。
  7. 交换缓冲:OpenGLES 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上,而屏幕外的渲染不需要调用此方法。
  8. 释放 EGL 环境:绘制结束后,不再需要使用 EGL 时,需要取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext 三个对象。

原来如此,原来 GLSurfaceView 里面,已经帮我们把 EGL 初始化好了,回到 glSurface.setRenderer() 的方法:

    // EGLConfig 对象.用于指定OpenGL颜色、深度、模版等设置
    if (mEGLConfigChooser == null) {
        mEGLConfigChooser = new SimpleEGLConfigChooser(true);
    }
<span class="token comment">// EGLContext 对象.用于提供EGLContext创建和销毁的处理</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>mEGLContextFactory <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    mEGLContextFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultContextFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// EGLSurface窗口对象. 用于提供EGLSurface创建和销毁的处理</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>mEGLWindowSurfaceFactory <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
    mEGLWindowSurfaceFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultWindowSurfaceFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
 
<span class="token comment">//开启 GLThread线程 </span>
mRenderer <span class="token operator">=</span> renderer<span class="token punctuation">;</span>
mGLThread <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GLThread</span><span class="token punctuation">(</span>mThisWeakRef<span class="token punctuation">)</span><span class="token punctuation">;</span>
mGLThread<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我们就知道,原来注释是这个意思,管理关键还得看一下 GL 线程做了什么。

2.2 GL 线程

我们去看看它和其他线程有什么区别:
在这里插入图片描述
实际上,它跟普通线程没有什么区别,也是继承Thread,关键就是 guradedRun() 方法,它实际上把 EGL 给初始化好,然后回调相关方法,让你去绘制,在一个 while 循环中,它做了哪些操作呢?

while(true){
	//初始化 egl,配置等信息
	mEglHelper.start()
	//回去 Surface,并配置到当前线程
	mElgHeper.createSurface()
	回调 onSurfaceCreated()
	回调 onSurfaceChanged()
	回调 onDrawFrame()
	//egl交换缓冲区,呈现画面
	mEglHelper.swap()
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

从这里看,guradedRun() 跟我们之前说的流程基本是一样的,还有非常熟悉的 onSurfaceCreated(), onSurfaceChanged() 和 onDrawFrame() 也是在 EGL 配置之后,再回调的。
这里我们也可以得出一个结论,就是 GL线程跟普通线程没什么区别,就是一个普通线程,只是按照 OpenGL 完整的绘图方式走了一遍,因此我们也可以自己自定义,也可以实现OpenGL 的绘制,从 glGenTextures 等绘图api 都是 native 方法也可以证明这一点。

2.3 为什么通常一个GL线程的texture等数据,在另一个GL线程没法用

回答这个问题之前,先看看纹理对象是怎么生成的,查看 GLES30.glGenTextures 方法:

// C function void glGenTextures ( GLsizei n, GLuint *textures )

public static native void glGenTextures(
int n,
int[] textures,
int offset
);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

看来跟 EGL 没关系,那它是怎么知道是 GL 线程去调,还是普通线程去调呢?它又是怎么把 glGenTextures 和 glDeleteTextures 对应到正确的线程上的呢?看源码:

在这里插入图片描述

在这里插入图片描述

实际上,它会先通过 getGlThreadSpecific() 这个方法去拿到一个 context,这个 context 实际上就是 EGL Context ,它是比较特殊的。什么意思呢,就是不同线程去拿,得到的 EGL Context 可能都不一样,这取决于给这个 EGL Context 是什么,我的理解是相当于线程类自己的局部变量,每个线程存储的 context 都不一样。

那EGL Context 是什么时候被设置进去的呢?
还记得 前面说到的 eglMakeCurrent() 这个方法吗?

egl.eglMakeCurrent(display, eglSurface, eglSurface, eglContext)

 
 
  • 1

实际上,第四个参数eglContext 最终会设置给 setGlThreadSpecific() 中的变量存储起来,给其 API 使用。
然后我们再看一下 eglMakeCurrent 做了什么:
在这里插入图片描述

可以得到以下总结:

  1. 获取当前线程 EGL context ,给底层使用
  2. 判断当前的 context 是否为 IS_CURRENT 状态,否则返回-1
  3. 如果 gl 是 IS_CURRENT 状态,但是不是当前线程,也return
  4. 如果 gl 不是 IS_CURRENT 状态,将 current 设置成非 IS_CURRENT 状态
  5. 将gl置为IS_CURRENT状态并将gl设置为当前线程的Thread Local的EGL Context

结论:

  1. 如果一个 EGL context 被一个线程使用 glMakeCurrent ,则它不能被另外一个线程 glMakeCurrent 了
  2. glMakeCurrent 之后, EGL Context 会跟当前的 EGL context 脱离关系

关于 texture 与 egl context 的关系,可以参考这篇文章 https://cloud.tencent.com/developer/article/1035505

因此,我们可以回答一下上面的问题:

  1. GL线程和普通线程有什么区别?: 没有区别,只是 GL线程 按照 OpenGL 的流程跑了一遍。
  2. texture所占用的空间是跟GL线程绑定的吗?:不是,跟 EGL context 绑定,跟 线程没关系。
  3. 为什么通常一个GL线程的texture等数据,在另一个GL线程没法用?:因为调用 OpenGL 接口,会先去获取 EGL context,而不同线程获取的状态是不一样的,而 texture 又放在 EGL context 中,因此无法在另一个 GL 线程使用。
  4. 为什么通常GL线程销毁后,为什么texture也跟着销毁了?:因为 GLSurfaceView 在销毁时,调用了 eglDestroyContext() 方法,销毁了 EGL context,从而 texture 也销毁了,跟 GL线程没关系
  5. 不同线程如何共享OpenGL数据:共享 EGL context,线程调用 eglCreateContext 时,传入另一个线程的 EGL Context。

三. 自定义 EGL 现成,使用SurfaceView+EGL 实现渲染

前面说到,GLSurfaceView 帮我们把 egl 的配置都弄好了,优点是使用简单,缺点是当我们想共享同个 EGL context,实现同个场景,不同 surface 的渲染时,GLSurfaceView 就使用了。
所以,我们自定义自己的 GLSurfaceView。

首先,创建一个类,让它也继承 SurfaceView,并创建一个线程,用来加载 EGL :

inner class EglSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
    init <span class="token punctuation">{<!-- --></span>
        holder<span class="token punctuation">.</span><span class="token function">addCallback</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span>l
    override fun <span class="token function">surfaceCreated</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> <span class="token class-name">SurfaceHolder</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
  
    <span class="token punctuation">}</span>

    override fun <span class="token function">surfaceChanged</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> <span class="token class-name">SurfaceHolder</span><span class="token punctuation">,</span> format<span class="token operator">:</span> <span class="token class-name">Int</span><span class="token punctuation">,</span> width<span class="token operator">:</span> <span class="token class-name">Int</span><span class="token punctuation">,</span> height<span class="token operator">:</span> <span class="token class-name">Int</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>

    <span class="token punctuation">}</span>

    override fun <span class="token function">surfaceDestroyed</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> <span class="token class-name">SurfaceHolder</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>

    <span class="token punctuation">}</span>
    inner <span class="token keyword">class</span> <span class="token class-name">EglThread</span><span class="token punctuation">(</span><span class="token keyword">private</span> val surface<span class="token operator">:</span> <span class="token class-name">Surface</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			 override fun <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
			 	<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
			 <span class="token punctuation">}</span>
	<span class="token punctuation">}</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

接着,就需要创建 EGL 了,这里我们通过模仿 GLSurfaceView 的 EGLHelper 方法,得到以下类:

inner class EglHelper {
        private var egl: EGL10? = null
        private var eglDisplay: EGLDisplay? = null
        private var eglSurface: EGLSurface? = null
        private var eglContext: EGLContext? = null
        fun initEgl(surface: Surface) {
            //1、得到Egl实例:
            val egl = EGLContext.getEGL() as EGL10
            //2、得到默认的显示设备(就是窗口)
            val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
            if (display === EGL10.EGL_NO_DISPLAY) {
                throw RuntimeException("eglGetDisplay failed")
            }
        <span class="token comment">//3、初始化默认显示设备</span>
        val displayVersions <span class="token operator">=</span> <span class="token class-name">IntArray</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>egl<span class="token punctuation">.</span><span class="token function">eglInitialize</span><span class="token punctuation">(</span>display<span class="token punctuation">,</span> displayVersions<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">throw</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">"eglInitialize failed"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        <span class="token comment">//4、设置显示设备的属性</span>
        val attr <span class="token operator">=</span> <span class="token function">intArrayOf</span><span class="token punctuation">(</span>
            <span class="token constant">EGL14</span><span class="token punctuation">.</span><span class="token constant">EGL_RED_SIZE</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_GREEN_SIZE</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_BLUE_SIZE</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_ALPHA_SIZE</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_DEPTH_SIZE</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_STENCIL_SIZE</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_RENDERABLE_TYPE</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_NONE</span>
        <span class="token punctuation">)</span>
        <span class="token comment">//5、从系统中获取对应属性的配置</span>
        val num_config <span class="token operator">=</span> <span class="token class-name">IntArray</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>egl<span class="token punctuation">.</span><span class="token function">eglChooseConfig</span><span class="token punctuation">(</span>display<span class="token punctuation">,</span> attr<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> num_config<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">throw</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">"eglChooseConfig failed"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>

        val numConfigs <span class="token operator">=</span> num_config<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>numConfigs <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">throw</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">"No configs match configSpec"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>

        val configs <span class="token operator">=</span> arrayOfNulls<span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">EGLConfig</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>numConfigs<span class="token punctuation">)</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>egl<span class="token punctuation">.</span><span class="token function">eglChooseConfig</span><span class="token punctuation">(</span>display<span class="token punctuation">,</span> attr<span class="token punctuation">,</span> configs<span class="token punctuation">,</span> numConfigs<span class="token punctuation">,</span> num_config<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">throw</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">"eglChooseConfig#2 failed"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>

        <span class="token comment">//6、创建EglContext</span>
        val  attrib_list <span class="token operator">=</span> <span class="token function">intArrayOf</span><span class="token punctuation">(</span>
            <span class="token constant">EGL14</span><span class="token punctuation">.</span><span class="token constant">EGL_CONTEXT_CLIENT_VERSION</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_NONE</span>
        <span class="token punctuation">)</span>
        val eglContext <span class="token operator">=</span> egl<span class="token punctuation">.</span><span class="token function">eglCreateContext</span><span class="token punctuation">(</span>display<span class="token punctuation">,</span>
            configs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
            <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_NO_CONTEXT</span><span class="token punctuation">,</span> attrib_list<span class="token punctuation">)</span>

        <span class="token comment">//7、创建渲染的Surface</span>
        val eglSurface <span class="token operator">=</span> egl<span class="token punctuation">.</span><span class="token function">eglCreateWindowSurface</span><span class="token punctuation">(</span>display<span class="token punctuation">,</span> configs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> surface<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>

        <span class="token comment">//8、绑定EglContext和Surface到显示设备中</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>egl<span class="token punctuation">.</span><span class="token function">eglMakeCurrent</span><span class="token punctuation">(</span>display<span class="token punctuation">,</span> eglSurface<span class="token punctuation">,</span> eglSurface<span class="token punctuation">,</span> eglContext<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
            <span class="token keyword">throw</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">"eglMakeCurrent fail"</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>egl <span class="token operator">=</span> egl
        <span class="token keyword">this</span><span class="token punctuation">.</span>eglDisplay <span class="token operator">=</span> display
        <span class="token keyword">this</span><span class="token punctuation">.</span>eglSurface <span class="token operator">=</span> eglSurface
        <span class="token keyword">this</span><span class="token punctuation">.</span>eglContext <span class="token operator">=</span> eglContext

    <span class="token punctuation">}</span>

    fun <span class="token function">swapBuffers</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        egl<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">eglSwapBuffers</span><span class="token punctuation">(</span>eglDisplay<span class="token punctuation">,</span> eglSurface<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>

    fun <span class="token function">destroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
        egl<span class="token operator">?</span><span class="token punctuation">.</span>apply <span class="token punctuation">{<!-- --></span>
            <span class="token function">eglMakeCurrent</span><span class="token punctuation">(</span>
                eglDisplay<span class="token punctuation">,</span>
                <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_NO_SURFACE</span><span class="token punctuation">,</span>
                <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_NO_SURFACE</span><span class="token punctuation">,</span>
                <span class="token constant">EGL10</span><span class="token punctuation">.</span><span class="token constant">EGL_NO_CONTEXT</span>
            <span class="token punctuation">)</span>
            <span class="token function">eglDestroySurface</span><span class="token punctuation">(</span>eglDisplay<span class="token punctuation">,</span> eglSurface<span class="token punctuation">)</span>
            eglSurface <span class="token operator">=</span> <span class="token keyword">null</span>
            <span class="token function">eglDestroyContext</span><span class="token punctuation">(</span>eglDisplay<span class="token punctuation">,</span> eglContext<span class="token punctuation">)</span>
            eglContext <span class="token operator">=</span> <span class="token keyword">null</span>
            <span class="token function">eglTerminate</span><span class="token punctuation">(</span>eglDisplay<span class="token punctuation">)</span>
            eglDisplay <span class="token operator">=</span> <span class="token keyword">null</span>
            egl <span class="token operator">=</span> <span class="token keyword">null</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

步骤比较简单,就是:

  1. 拿到Egl实例 :EGLContext.getEGL()
  2. 得到默认的显示设备(就是窗口):egl.eglGetDisplay
  3. 初始化默认显示设备:egl.eglInitialize
  4. 设置显示设备的属性和配置到 egl 中:egl.eglChooseConfig
  5. 创建EglContext:egl.eglCreateContext
  6. 创建渲染的Surface:egl.eglCreateWindowSurface
  7. 绑定EglContext和Surface到显示设备中:egl.eglMakeCurrent
  8. 交换缓冲区,渲染:egl.eglSwapBuffers

然后我们把 EhlHeper 跟线程结合起来,完整的代码如下:

/**
* 自定义一个 SurfaceView,里面的线程模拟 EGL 环境
*/
inner class EglSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

init {
holder.addCallback(this)
}

private var eglThread: EglThread? = null

override fun surfaceCreated(holder: SurfaceHolder) {
eglThread = EglThread(holder.surface)
eglThread?.start()
}

override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
eglThread?.changeSize(width, height)
}

override fun surfaceDestroyed(holder: SurfaceHolder) {
eglThread?.release()
}

/**
* gl线程,里面包含 EGL 创建流程
*/

inner class EglThread(private val surface: Surface) : Thread() {
private var isExit = false
private var isFirst = true
private var isSizeChange = false
private var eglHelper: EglHelper? = null
private var width = 0
private var height = 0
override fun run() {
super.run()
eglHelper = EglHelper().apply {
initEgl(surface)
}
while (true) {
if (isExit) {
release()
break
}

           <span class="token keyword">if</span> <span class="token punctuation">(</span>isFirst<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
               isFirst <span class="token operator">=</span> <span class="token boolean">false</span>
               <span class="token comment">//回调给主类</span>
               <span class="token keyword">this</span><span class="token annotation punctuation">@L8_ShapeRender.onSurfaceCreated</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
           <span class="token punctuation">}</span>
           <span class="token keyword">if</span> <span class="token punctuation">(</span>isSizeChange<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
               isSizeChange <span class="token operator">=</span> <span class="token boolean">false</span>
               <span class="token keyword">this</span><span class="token annotation punctuation">@L8_ShapeRender.onSurfaceChanged</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> width<span class="token punctuation">,</span> height<span class="token punctuation">)</span>
           <span class="token punctuation">}</span>
           eglHelper<span class="token operator">?</span><span class="token punctuation">.</span>let <span class="token punctuation">{<!-- --></span>
               <span class="token keyword">this</span><span class="token annotation punctuation">@L8_ShapeRender.onDrawFrame</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span>
               it<span class="token punctuation">.</span><span class="token function">swapBuffers</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
           <span class="token punctuation">}</span>
           <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span>
               <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span>
           <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token operator">:</span> <span class="token class-name">Exception</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
           <span class="token punctuation">}</span>
       <span class="token punctuation">}</span>
   <span class="token punctuation">}</span>

   fun <span class="token function">release</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
       isExit <span class="token operator">=</span> <span class="token boolean">true</span>
       eglHelper<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">destroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
       surface<span class="token punctuation">.</span><span class="token function">release</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
   <span class="token punctuation">}</span>

   fun <span class="token function">changeSize</span><span class="token punctuation">(</span>width<span class="token operator">:</span> <span class="token class-name">Int</span><span class="token punctuation">,</span> height<span class="token operator">:</span> <span class="token class-name">Int</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
       <span class="token keyword">this</span><span class="token punctuation">.</span>width <span class="token operator">=</span> width
       <span class="token keyword">this</span><span class="token punctuation">.</span>height <span class="token operator">=</span> height
       isSizeChange <span class="token operator">=</span> <span class="token boolean">true</span>
   <span class="token punctuation">}</span>

}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

都比较好理解,可以看到,我们也通过 onSurfaceCreated,onSurfaceChanged 和 onDrawFrame 回调给外部,这样,着色器相关的,就不用改变啦。
看一下效果,可以正常运行:
在这里插入图片描述

参考:
https://cloud.tencent.com/developer/article/1035505
https://cloud.tencent.com/developer/article/1899820

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值