GLSurfaceView源码分析以及简单使用

一、
GLSurfaceView 如果我们没有使用过,从名字可以看出其与OpenGL以及Surfaceview有关,GLSurfaceView有以下特点:
1、管理surface,一块特别的内存区域给Android 视图系统。
2、管理EGL展示,能够保证OpenGL渲染到Surface上。
3、接收自定义的Render,这个Render做实际的渲染。
4、渲染是在专门的GL线程,从UI线程中分离出来。
5、支持按需或者不间断的渲染模式。

我这里先展示下初步学习使用绘制一个三角形
1、xml 中引用

 <android.opengl.GLSurfaceViewandroid:id="@+id/glview"android:layout_width="300dp"android:layout_height="300dp" />

2、MainActivity

public class MainActivity extends AppCompatActivity {private GLSurfaceView mGLSurfaceView;private MyRender myRender;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mGLSurfaceView = findViewById(R.id.glview);myRender = new MyRender();//设置渲染mGLSurfaceView.setRenderer(myRender);//设置按需渲染模式mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);}@Overrideprotected void onPause() {mGLSurfaceView.onPause();super.onPause();}@Overrideprotected void onResume() {mGLSurfaceView.onResume();super.onResume();}@Overrideprotected void onStop() {super.onStop();}
}

MyRender.java

public class MyRender implements GLSurfaceView.Renderer {

private static final String TAG = MyRender.class.getSimpleName();private float[] mTriangleArray = {// X, Y, Z 这是一个等边三角形-0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0};@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {// 设置背景颜色gl.glClearColor(1.0f, 1.0f, 1.0f, 0.5f);// 启用顶点数组(否则glDrawArrays不起作用)gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
}@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {Log.d(TAG, " onSurfaceChanged width " + width + " height : " + height);gl.glViewport(0, 0, width, height);
}@Override
public void onDrawFrame(GL10 gl) {Log.d(TAG, "onDrawFrame ");gl.glClear(GL10.GL_COLOR_BUFFER_BIT);gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);FloatBuffer mTriangleBuffer = BufferUtil.floatToBuffer(mTriangleArray);gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mTriangleBuffer);gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);//gl.glLoadIdentity();// 平移 (矩阵相乘)//   gl.glTranslatef(-0.2f, 0.3f, 0f);}static class BufferUtil {public static FloatBuffer mBuffer;public static FloatBuffer floatToBuffer(float[] a) {// 先初始化buffer,数组的长度*4,因为一个float占4个字节ByteBuffer mbb = ByteBuffer.allocateDirect(a.length * 4);// 数组排序用nativeOrdermbb.order(ByteOrder.nativeOrder());mBuffer = mbb.asFloatBuffer();mBuffer.put(a);mBuffer.position(0);return mBuffer;}
}

效果图如下:

源码分析:

    public void setRenderer(Renderer renderer) {// 检测渲染线程状态,如果已经创建了,直接抛出异常 checkRenderThreadState();if (mEGLConfigChooser == null) {mEGLConfigChooser = new SimpleEGLConfigChooser(true);}//创建EGL 上下文环境if (mEGLContextFactory == null) {mEGLContextFactory = new DefaultContextFactory();}// 创建承载Surface的window factoryif (mEGLWindowSurfaceFactory == null) {mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();}mRenderer = renderer;//开启渲染线程//mThisWeakRef 是弱引用,持有当前的GLSurfaceView对象mGLThread = new GLThread(mThisWeakRef);mGLThread.start();}

GLThread 的run方法中主要调用了guardedRun方法,看下其实现:

 private void guardedRun() throws InterruptedException {while (true) {synchronized (sGLThreadManager) {while (true) {//是否应该退出if (mShouldExit) {return;}//队列不空,取出if (! mEventQueue.isEmpty()) {event = mEventQueue.remove(0);break;}// Update the pause state.boolean pausing = false;if (mPaused != mRequestPaused) {pausing = mRequestPaused;mPaused = mRequestPaused;sGLThreadManager.notifyAll();if (LOG_PAUSE_RESUME) {Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());}}// Do we need to give up the EGL context?if (mShouldReleaseEglContext) {if (LOG_SURFACE) {Log.i("GLThread", "releasing EGL context because asked to tid=" + getId());}stopEglSurfaceLocked();stopEglContextLocked();mShouldReleaseEglContext = false;askedToReleaseEglContext = true;}// Have we lost the EGL context?if (lostEglContext) {stopEglSurfaceLocked();stopEglContextLocked();lostEglContext = false;}// When pausing, release the EGL surface:if (pausing && mHaveEglSurface) {if (LOG_SURFACE) {Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());}stopEglSurfaceLocked();}// When pausing, optionally release the EGL Context:if (pausing && mHaveEglContext) {GLSurfaceView view = mGLSurfaceViewWeakRef.get();boolean preserveEglContextOnPause = view == null ?false : view.mPreserveEGLContextOnPause;if (!preserveEglContextOnPause) {stopEglContextLocked();if (LOG_SURFACE) {Log.i("GLThread", "releasing EGL context because paused tid=" + getId());}}}// Have we lost the SurfaceView surface?if ((! mHasSurface) && (! mWaitingForSurface)) {if (LOG_SURFACE) {Log.i("GLThread", "noticed surfaceView surface lost tid=" + getId());}if (mHaveEglSurface) {stopEglSurfaceLocked();}mWaitingForSurface = true;mSurfaceIsBad = false;sGLThreadManager.notifyAll();}// Have we acquired the surface view surface?if (mHasSurface && mWaitingForSurface) {if (LOG_SURFACE) {Log.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());}mWaitingForSurface = false;sGLThreadManager.notifyAll();}if (doRenderNotification) {if (LOG_SURFACE) {Log.i("GLThread", "sending render notification tid=" + getId());}mWantRenderNotification = false;doRenderNotification = false;mRenderComplete = true;sGLThreadManager.notifyAll();}// Ready to draw?if (readyToDraw()) {// If we don't have an EGL context, try to acquire one.if (! mHaveEglContext) {if (askedToReleaseEglContext) {askedToReleaseEglContext = false;} else {try {mEglHelper.start();} catch (RuntimeException t) {sGLThreadManager.releaseEglContextLocked(this);throw t;}mHaveEglContext = true;createEglContext = true;sGLThreadManager.notifyAll();}}if (mHaveEglContext && !mHaveEglSurface) {mHaveEglSurface = true;createEglSurface = true;createGlInterface = true;sizeChanged = true;}if (mHaveEglSurface) {if (mSizeChanged) {sizeChanged = true;w = mWidth;h = mHeight;mWantRenderNotification = true;if (LOG_SURFACE) {Log.i("GLThread","noticing that we want render notification tid="+ getId());}// Destroy and recreate the EGL surface.createEglSurface = true;mSizeChanged = false;}mRequestRender = false;sGLThreadManager.notifyAll();if (mWantRenderNotification) {wantRenderNotification = true;}break;}}// By design, this is the only place in a GLThread thread where we wait().if (LOG_THREADS) {Log.i("GLThread", "waiting tid=" + getId()+ " mHaveEglContext: " + mHaveEglContext+ " mHaveEglSurface: " + mHaveEglSurface+ " mFinishedCreatingEglSurface: " + mFinishedCreatingEglSurface+ " mPaused: " + mPaused+ " mHasSurface: " + mHasSurface+ " mSurfaceIsBad: " + mSurfaceIsBad+ " mWaitingForSurface: " + mWaitingForSurface+ " mWidth: " + mWidth+ " mHeight: " + mHeight+ " mRequestRender: " + mRequestRender+ " mRenderMode: " + mRenderMode);}sGLThreadManager.wait();}} // end of synchronized(sGLThreadManager)if (event != null) {event.run();event = null;continue;}if (createEglSurface) {if (LOG_SURFACE) {Log.w("GLThread", "egl createSurface");}if (mEglHelper.createSurface()) {synchronized(sGLThreadManager) {mFinishedCreatingEglSurface = true;sGLThreadManager.notifyAll();}} else {synchronized(sGLThreadManager) {mFinishedCreatingEglSurface = true;mSurfaceIsBad = true;sGLThreadManager.notifyAll();}continue;}createEglSurface = false;}if (createGlInterface) {gl = (GL10) mEglHelper.createGL();createGlInterface = false;}if (createEglContext) {if (LOG_RENDERER) {Log.w("GLThread", "onSurfaceCreated");}GLSurfaceView view = mGLSurfaceViewWeakRef.get();if (view != null) {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}createEglContext = false;}if (sizeChanged) {if (LOG_RENDERER) {Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");}GLSurfaceView view = mGLSurfaceViewWeakRef.get();if (view != null) {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");view.mRenderer.onSurfaceChanged(gl, w, h);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}sizeChanged = false;}if (LOG_RENDERER_DRAW_FRAME) {Log.w("GLThread", "onDrawFrame tid=" + getId());}{GLSurfaceView view = mGLSurfaceViewWeakRef.get();if (view != null) {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");view.mRenderer.onDrawFrame(gl);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}}int swapError = mEglHelper.swap();switch (swapError) {case EGL10.EGL_SUCCESS:break;case EGL11.EGL_CONTEXT_LOST:if (LOG_SURFACE) {Log.i("GLThread", "egl context lost tid=" + getId());}lostEglContext = true;break;default:// Other errors typically mean that the current surface is bad,// probably because the SurfaceView surface has been destroyed,// but we haven't been notified yet.// Log the error to help developers understand why rendering stopped.EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", swapError);synchronized(sGLThreadManager) {mSurfaceIsBad = true;sGLThreadManager.notifyAll();}break;}if (wantRenderNotification) {doRenderNotification = true;wantRenderNotification = false;}}} finally {/** clean-up everything...*/synchronized (sGLThreadManager) {stopEglSurfaceLocked();stopEglContextLocked();}}}

1、方法中加了 synchronized (sGLThreadManager) ,进行线程控制
2、if (! mEventQueue.isEmpty()) {
event = mEventQueue.remove(0);
break;
}
队列中取出一个元素,break跳出内循环。
3、 if (readyToDraw()) 满足该条件,进行是否满足条件准备绘制

rivate boolean readyToDraw() {return (!mPaused) && mHasSurface && (!mSurfaceIsBad)&& (mWidth > 0) && (mHeight > 0)&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));}

同时要满足这么多条件,尤其注重最后一个条件,如果渲染模式是RENDERMODE_CONTINUOUSLY(不设置默认就是这种情形)或者请求绘制(mRequestRender 为 true)
如果不满足该条件,调用 sGLThreadManager.wait(),阻塞该线程。
4、回调onSurfaceOnCreated方法

                if (createEglContext) {if (LOG_RENDERER) {Log.w("GLThread", "onSurfaceCreated");}GLSurfaceView view = mGLSurfaceViewWeakRef.get();if (view != null) {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}createEglContext = false;}

第9行回调 渲染回调 的 onSurfaceCreated 方法,createEglContext = false,说明只回调一次。
5、onSurfaceChanged

                if (sizeChanged) {if (LOG_RENDERER) {Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");}GLSurfaceView view = mGLSurfaceViewWeakRef.get();if (view != null) {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");view.mRenderer.onSurfaceChanged(gl, w, h);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}sizeChanged = false;}

6、onDrawFrame:
view.mRenderer.onDrawFrame(gl)

                       GLSurfaceView view = mGLSurfaceViewWeakRef.get();if (view != null) {try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");view.mRenderer.onDrawFrame(gl);} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}

7、渲染到surface

  int swapError = mEglHelper.swap();/*** Display the current render surface.* @return the EGL error code from eglSwapBuffers.*/public int swap() {if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {return mEgl.eglGetError();}return EGL10.EGL_SUCCESS;}

这个EglSurface是什么?
传入的是Surfaceview的getHolder

          if (view != null) {mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,mEglDisplay, mEglConfig, view.getHolder());}

最后注意,实现onPause,以及onResume方法,阻塞以及恢复GL渲染线程。

   public void onPause() {synchronized (sGLThreadManager) {if (LOG_PAUSE_RESUME) {Log.i("GLThread", "onPause tid=" + getId());}mRequestPaused = true;sGLThreadManager.notifyAll();while ((! mExited) && (! mPaused)) {if (LOG_PAUSE_RESUME) {Log.i("Main thread", "onPause waiting for mPaused.");}try {sGLThreadManager.wait();} catch (InterruptedException ex) {Thread.currentThread().interrupt();}}}}

先唤醒线程再阻塞。

GLSurfaceView源码分析以及简单使用相关推荐

  1. Android之HandlerThread源码分析和简单使用(主线程和子线程通信、子线程和子线程通信)

    1.先熟悉handler方式实现主线程和子线程互相通信方式,子线程和子线程的通信方式 如果不熟悉或者忘记了,请参考我的这篇博客     Android之用Handler实现主线程和子线程互相通信以及子 ...

  2. hiredis源码分析与简单封装

    hiredis Hiredis是一个开源C库函数,提供了基本的操作redis 函数, 如数据库连接.发送命令.释放资源等等 1.hiredis net hiredis 本身就是做了跨平台的代码,c语言 ...

  3. Mongoose源码分析之--简单的服务器搭建(C语言)

    测试半天一直不行,发现原版少写个return null, 什么都不需要处理 可以实现web 由于在IOS终端设备上播放实时的TS流,要使用MPMoviePlayerController控件,必须采用H ...

  4. Python wordcloud词云:源码分析及简单使用

    Python版本的词云生成模块从2015年的v1.0到现在,已经更新到了v1.7. 下载请移步至:https://pypi.org/project/wordcloud/ wordcloud简单应用: ...

  5. 集合之TreeMap源码分析,简单介绍什么是红黑树,SortedMap和NavigableMap之间的关系和区别

    TreeMap底层实现 1. TreeMap底层实现是红黑树,并且树的节点是内部类Entry类型 2. 红黑树的定义 ① 每个节点是黑色或红色:②根节点是黑色:③所有的叶节点是黑色,不是真正的叶节点, ...

  6. 《ASCE1885的源码分析》の简单的进程封装类

    一个简单的进程封装类,该类允许我们新建一个远程进程,并对其进行控制. 进程类CProcess的头文件如下: class CProcess{ public: PROCESS_INFORMATION Pr ...

  7. Snackbar源码分析

    目录介绍 1.最简单创造方法 1.1 Snackbar作用 1.2 最简单的创建 1.3 Snackbar消失的几种方式 2.源码分析 2.1 Snackbar的make方法源码分析 2.2 对Sna ...

  8. LIVE555再学习 -- testH264VideoStreamer 源码分析

    上一篇文章我们已经讲了一部分: testH264VideoStreamer 重复从 H.264 基本流视频文件(名为"test.264")中读取,并使用 RTP 多播进行流式传输. ...

  9. Dialog源码分析

    目录介绍 1.简单用法 2.AlertDialog源码分析 2.1 AlertDialog.Builder的构造方法 2.2 通过AlertDialog.Builder对象设置属性 2.3 build ...

最新文章

  1. 2021-2027年中国一体化预制泵站行业研究及前瞻分析报告
  2. Linux: 使用bash命令ls按时间排序
  3. 【图灵】iOS技能书单——入门+进阶+精通
  4. mysql常用命令行操作(二):表和库的操作、引擎、聚合函数
  5. css z-index层重叠顺序
  6. ASP.NET中进行消息处理(MSMQ)
  7. attr与prop的区别
  8. java char 计算_经典Java面试题之Java中Char类型的运算
  9. ReentrantLock与synchronized的区别(最直观)
  10. mysql服务性能优化—my.cnf配置说明详解
  11. 在eclipse环境下配置OpenCV环境
  12. js中 json对象与json字符串相互转换的几种方式
  13. 前端——HTML百度首页制作
  14. 【图像处理】高斯模糊、高斯函数、高斯核、高斯卷积操作
  15. npcap lookback adapter回环网卡是什么 它的作用是什么
  16. 芯烨网口小票打印机,使用PHP打印小票
  17. rpm mysql nokey_rpm包时遇到Header V3 DSA signature: NOKEY时解决办法
  18. VB中传值(ByVal)和传地址(ByRef)的区别
  19. oracle 字段是合法日期,Oracle中日期字段的处理
  20. 微信小程序入门与实战之更多电影列表与电影搜索

热门文章

  1. KAWAI 钢琴编号、年代、型号表
  2. Power BI Desktop 10月更新
  3. java中比较两个文件的大小_Java实现获取文件大小的几种方法
  4. fpga结构主体_两大FPGA公司的“AI技术路线”
  5. 带文件卖面,老坛酸菜面重回超市货架!你会买账吗?京东、淘宝依然屏蔽
  6. 腾讯视频已上线超前点播选集解锁
  7. 这个城市推出黄金“外卖”!价值低于5.8万元的,只能摩托车配送...
  8. 一加8系列新机有望亮相CES 2020:全系支持5G网络
  9. 摩拜单车又涨价了!真的要骑不起了
  10. 人走茶凉!三星关闭最后一家中国手机工厂 因为打不过其他国产厂商?