GLSurfaceView类是继承自SurfaceView的,并且实现了SurfaceHolder.Callback2接口。

GLSurfaceView内部管理着一个surface,专门负责OpenGL渲染。GLSurfaceView内部通过GLThread和EGLHelper

为我们完成了EGL环境渲染和渲染线程的创建及管理,使我们只需要在外部实现渲染器Renderer

即可使用OpenGL ES进行绘图。所以,了解GLSurfaceView的内部逻辑对于我们使用OpenGL ES来绘图还是很有必要的。

GLSurfaceView的初始化

首先,我们在最外使用GLSurfaceView的时候,实例化GLSurfaceView时其会对自身进行初始化:

1
2
3
4
private void init() {
SurfaceHolder holder = getHolder();
holder.addCallback(this);
}

可以看到这里主要对自身的Holder设置了Callback2接口,然后在自身内部实现了Callback2的四个回调方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void surfaceCreated(SurfaceHolder holder) {
mGLThread.surfaceCreated();
}
public void surfaceDestroyed(SurfaceHolder holder) {
mGLThread.surfaceDestroyed();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
mGLThread.onWindowResize(w,h);
}
public void surfaceRedrawNeeded(SurfaceHolder holder) {
if(mGLThread != null) {
mGLThread.requestRenderAndWait();
}
}

可以看到四个回调方法最终都共同指向了GLThread,其实这里的GLThread是GLSurfaceView内部的绘制线程,

使绘制工作可以独立于主线程(GLThread承担了SurfaceView中大部分的工作,具体的后面再细说)。

至此,GLSurfaceView的初始化工作完成,但到这里渲染环境的初始化工作却还没有开始。

当实例化GLSurfaceView后,接着都会进行两个步骤,那就是设置OpenGL的版本号和渲染器:

1
2
this.setEGLContextClientVersion(2); //设置OpenGL的版本号2.0
setRenderer(new MyRenderer()); //设置OpenGL的渲染器

这里重点要看setRenderer()这个方法,通过这个方法我们不仅为GLSurfaceView设置了renderer,

还会对EGL环境进行初步的设置工作,最后还有调起GLThread,开启绘制线程。所以在GLSurfaceView的整个生命周期中,

setRenderer()只能被调用一次。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
}

设置渲染器时,首先会对渲染线程的状态进行检查,如果GLThread已经存在即setRenderer已经被调用过,

则抛出异常,这也是为什么setRenderer()只能在GLSurfaceView的整个生命周期中调用一次。

然后会对mEGLConfigChooser、mEGLContextFactory、mEGLWindowSurfaceFactory进行实例化,

再实例化GLThread并执行该渲染线程,其中传入的mThisWeakRef是当前GLSurfaceView的弱引用。

GLThread——GLSurfaceView内部的渲染线程

GLThread是一个继承Thread的类,主要的运行代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
while(true) {
synchronized(sGLThreadManager) {
while(true) {
......
mEglHelper.start();
......
sGLThreadManager.wait();
}
}
......
mEglHelper.createSurface();
......
gl = (GL10) mEglHelper.createGL();
......
mRenderer.onSurfaceCreated(gl, mEglHelper.getEglConfig());
......
mRenderer.onSurfaceChanged(gl, w, h);
......
mRenderer.onDrawFrame(gl);
}

其中被synchronized(sGLThreadManager)同步的代码块是用于线程之间通信的,外部线程可以停止和恢复这个线程。

这部分同步代码块的主要职责是创建EGL环境,GLThread对EGL的所有操作都是通过EglHelper来实现的。

EglHelper——创建EGL环境的帮助类

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
private static class EglHelper {
/**
* Initialize EGL for a given configuration spec
*/
public void start() {
......
mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
......
mEgl.eglInitialize(mEglDisplay, version);
......
mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
}
/**
* Create an egl surface for the current SurfaceHolder surface. If a surfce
* already exists, destroy it before creating the new surface.
*/
public boolean createSurface() {
......
/*
* The window size has changed, so we need to create a new surface.
*/
destroySurfaceImp();
......
/*
* Create an EGL surface we can render into.
*/
mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay,
mEglConfig, view.getHolder());
......
/*
* Before we can issue GL commands, we need to make sure the context
* is current and bound to a surface.
*/
mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
......
}
/**
* Create a GL object for the current EGL context
*/
GL createGL() {
GL gl = mEglContext.getEGL();
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
......
gl = view.mGLWrapper.wrap(gl);
......
gl = GLDebugHelper.wrap(gl, configFlags, log);
return gl;
}
......
}

所以整体看来,就是GLThread在其同步代码块中通过mEglHelper.start()对EGL环境进行初始化,

而后在同步块外部先调用mEglHelper.createSurface()为当前的Surface创建一个EGL的surface,

再通过mEglHelper.createGL()创建一个GL对象,最后就将GL对象传递给renderer的三个回调方法

onSurfaceCreated()、onSurfaceChanged()、onDrawFrame()。所以我们在最外面只需要在这三个

回调方法中实现我们真正要绘制的东西即可。

再说GLThread

我们再来看看GLThread内部其他的一些方法,在最开始的时候我们说到给holder传入一个回调接口,

而这个回调接口的四个方法的实现最终都指向了GLThread的内部方法,那么我们先来看看这四个方法

在GLThread内部的实现:

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
// GLThread的内部方法
public void surfaceCreated() {
synchronized(sGLThreadManager) {
mHasSurface = true;
mFinishedCreatingEglSurface = false;
sGLThreadManager.notifyAll();
while(mWaitingForSurface && !mFinishedCreatingEglSurface && !mExited) {
try {
sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrput();
}
}
}
}
public void surfaceDestroyed() {
synchronized(sGLThreadManager) {
mHasSurface = true;
sGLThreadManager.notifyAll();
while(!mWaitingForSurface && !mExited) {
try {
sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrput();
}
}
}
}
public void onWindowResize(int w, int h) {
synchronized(sGLThreadManager) {
mWidth = w;
mHeight = h;
mSizeChanged = true;
mRequestRender = true;
mRenderComplete = false;
if(Thread.currentThread() == this) {
return;
}
sGLThreadManager.notifyAll();
while(!mExited && !mPaused && !mRenderComplete && ableToDraw) {
try {
sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrput();
}
}
}
}
public void requestRenderAndWait() {
synchronized(sGLThreadManager) {
if(Thread.currentThread() == this) {
return;
}
mWantRenderNotification = true;
mRequestRender = true;
mRenderComplete = false;
sGLThreadManager.notifyAll();
while(!mExited && !mPaused && !RenderComplete && ableToDraw()) {
try {
sGLThreadManager.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrput();
}
}
}
}

这些供外部调用的方法其最终都只是改变GLThread的一些内部标志,然后通过sGLThreadManager.notifyAll

唤醒渲染线程,GLThread内部即通过判断这些标志位去执行对应的参数设置操作,这些参数设置操作都在

synchronized(sGLTHreadManager)同步的循环体中进行,当最后已经准备好绘制工作,则跳出内循环体,

在外循环体中调用renderer的回调方法onDrawFrame去真正实现绘制,核心代码如下:

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
while(true) {
synchronized(sGLThreadManager) {
while(true) {
......
mEglHelper.start();
......
// 如果已经准备好绘制环境
if(readyToDraw()) {
// 再一次检查EglContext和EglSurface的状态,满足则跳出同步代码块的循环体
// If we don't have an EGL context, try to acquire one
if(!mHaveEglContext) {
......
if(sGLThreadManager.tryAcquireEglContextLocked(this)) {
mEglHelper.start();
}
}
if(mHaveEglSurface) {
......
break; // 跳出同步代码块的循环体
}
}
......
sGLThreadManager.wait();
}
}
......
// 调用Renderer进行绘制
mEglHelper.createSurface();
gl = (GL10) mEglHelper.createGL();
mRenderer.onSurfaceCreated(gl, mEglHelper.getEglConfig());
mRenderer.onSurfaceChanged(gl, w, h);
mRenderer.onDrawFrame(gl);
}

最后终于回到了我们的渲染器Renderer,然后,我们只需要在外部实现Renderer的三个回调方法便

可使用OpenGL ES来绘图了。当然,在我们弄懂了GLSurfaceView的原理后自己动手封装一个也是没有

问题的。不过这里其实没有说得很明白的还有EGL这部分,后面有时间将补上。


源码分析-GLSurfaceView的内部实现相关推荐

  1. rust里mp5a4_Rust源码分析:channel内部mpsc队列

    首先,之前的upgrade过程中内存的回收要稍微注意下.因为Receiver现在指向shared::Packet之后,那个new_port需要被析构,也就是调用drop函数,我们看下drop的实现: ...

  2. Java8 IdentityHashMap 源码分析

    在讲这个数据结构之前,我们先来看一段代码: public static void main(String[] args) {IdentityHashMap<String, Integer> ...

  3. Java8 CopyOnWriteArrayList 源码分析

    一.CopyOnWriteArrayList 概述 1.1 概念概述 CopyOnWriteArrayList 是 juc 包下一个线程安全的并发容器,底层使用数组实现.CopyOnWrite 顾名思 ...

  4. Mybatis源码分析: MapperMethod功能讲解

    canmengqian </div><!--end: blogTitle 博客的标题和副标题 --> <div id="navigator"> ...

  5. GLSurfaceView源码分析以及简单使用

    GLSurfaceView源码分析以及简单使用 一. GLSurfaceView 如果我们没有使用过,从名字可以看出其与OpenGL以及Surfaceview有关,GLSurfaceView有以下特点 ...

  6. [源码分析] 从FlatMap用法到Flink的内部实现

    [源码分析] 从FlatMap用法到Flink的内部实现 0x00 摘要 本文将从FlatMap概念和如何使用开始入手,深入到Flink是如何实现FlatMap.希望能让大家对这个概念有更深入的理解. ...

  7. 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化

    人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...

  8. 霸榜巨作、阿里内部顶级专家整理(Redis 5设计与源码分析)

    前言 在开源界,高性能服务的典型代表就是Nginx和Redis.纵观这两个软件的源码,都是非常简洁高效的,也都是基于异步网络I/O机制的,所以对于要学习高性能服务的程序员或者爱好者来说,研究这两个网络 ...

  9. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

最新文章

  1. 基于点云描述子的立体视觉里程计快速鲁棒的位置识别方法
  2. 交换机自动学习vlan
  3. [MicroPython]TurnipBit开发板旋转按钮控制直流电机转速
  4. Homestead 集成开发环境配置
  5. Linux系统运维成长记
  6. 投影参数_色彩极致3-怎么调校投影机的参数
  7. MariaDB 在 RedHat Linux 上的安装过程以及 MySQL 相关命令的使用
  8. 脚本文件不变色_LoadRunner脚本开发
  9. STM32学习(1)-资料查找,STM32简介,STM32选型以及芯片内部结构图
  10. U8 13.0 - 查询报表慢,程序未响应
  11. 脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?
  12. Android-TextView添加字体库
  13. 微信小程序开发:各种页面特效集合(持续更新)
  14. 巴布亚企鹅需要开启sshd的root权限
  15. 记录下2345锁定主页解决方案
  16. 临沧黄衣阿佤-中国佤民族中一支穿黄色衣服的佤族群体
  17. Windows消息响应机制之四:PostQuitMessage和GetMessage函数
  18. (转) 在C#中使用WIA获取扫描仪数据(三、利用Filter处理图片)
  19. xHiveAI-A311D:AI开发套件
  20. Spring项目使用mkcert制作自签名证书

热门文章

  1. linux文件编辑操作,Linux下文本编辑及其文件操作
  2. java数组与字符串编程及答案_04747_Java语言程序设计(一)_第4章_数组和字符串...
  3. php中介模式,中介者模式(Mediator pattern)详解及PHP实现
  4. lstm timestep一般是多少_请问rnn和lstm中batchsize和timestep的区别是什么?
  5. 站在巨人的肩膀上,Adrian与dlib中face_recongnition模块的贡献者Adam的采访部分翻译
  6. Eigen(1):Matrix模板类
  7. access突然需要登录_早知道早好,微信小程序登录开发需要注意的事项
  8. 几种和边框相关的CSS样式修改
  9. Pliops XDP(Extreme Data Processor)数据库存储设计的新型加速硬件
  10. xBIM 实战01 在浏览器中加载IFC模型文件