源码分析-GLSurfaceView的内部实现

 

转载自:https://blog.csdn.net/Darlingqiang/article/details/80274558

 

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这部分,后面有时间将补上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值