Android初始化OpenGL ES,并且分析Renderer子线程原理

Android使用OpenGL ES需要搭配GLSurfaceView、Renderer,当然也可以使用TextureView,不过TextureView没有GLSurfaceView的OpenGL ES初始化操作,所以使用起来有些麻烦,需要自定义OpenGL初始化,本文使用GLSurfaceView初始化OpenGL ES。

首先讲一下Android中View,GLSurfaceView和TextureView等的区别吧

  • View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。
  • SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;**是view的子类,类似使用双缓机制,在新的线程中更新画面,所以刷新界面速度比view快。**其缺点是不能做变形和动画,也不能随屏幕的变化而变化,另外不能在其上面覆盖其它的SurfaceView。
  • GLSurfaceView:是surfaceview的子类,在其基础上封装了egl环境管理,以及render线程。专用于3D游戏开发的视图,OpenGL ES专用
  • TextureView:它也是继承自View,只能运行中硬件加速窗口。它的功能类似于SurfaceView + SurfaceTexture,它内部包含一个SurfaceTexture,它可以让Camera的数据和显示分离,比如需要做二次处理时,如Camera吧采集的数据发送给SurfaceTexture(比如做个美颜),SurfaceTexture处理后传给TextureView显示。TextureView可以做view的变形和动画。一般它是在主线程上做处理(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。

##初始化OpenGL##

一般步骤如下:

  • 创建GLSurfaceView实例
  • 检查设备是否支持OpenGL ES 2.0
  • 配置OpenGL ES
  • 创建Renderer

前三步的代码可参考如下:

	@Override
	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //实例化一个GLSurfaceView
        mGLSurfaceView = new GLSurfaceView(this);

        //检查设备是否支持OpenGL ES 2.0
        final ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
        final boolean supportES2 = configurationInfo.reqGlEsVersion >= 0x00020000;

        //配置OpenGL ES,主要是版本设置和设置Renderer,Renderer用于执行OpenGL的绘制
        if (supportES2) {
            mGLSurfaceView.setEGLContextClientVersion(2);
            mGLSurfaceView.setRenderer(new MyRenderer());
            renderSet = true;
        } else {
            Toast.makeText(getApplicationContext(),"不支持OpenGL ES 2.0版本",Toast.LENGTH_SHORT).show();
            return;
        }
		//在屏幕上显示GLSurfaceView
		setContentView(mGLSurfaceView);
    }

创建Renderer渲染器的代码如下:

	public class MyRenderer implements GLSurfaceView.Renderer {
        //当Surface创建后会调用此方法
        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            glClearColor(1.0f,0.0f,0.0f,0.0f);//清空屏幕的颜色,本例为红色
        }

        //当Surface创建成功或尺寸改变时都调用此方法
        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            glViewport(0,0,width,height);
        }

        //每绘制一帧都会调用此方法
        @Override
        public void onDrawFrame(GL10 gl) {
            //清空颜色缓冲区,然后使用glClearColor()方法设置填充屏幕的颜色
            glClear(GL_COLOR_BUFFER_BIT);
        }
    }

GL10 gl这个参数是OpenGL ES 1.0遗留下来的,如果使用OpenGL ES 2.0以上版本可以忽略它

现在运行起来屏幕会显示空白的红色窗口,初始化OpenGL就是这么简单

##Renderer子线程原理

之前讲了GLSurfaceView是在新的线程更新画面,也就是执行Renderer,现在通过源码来分析一下是如何做到的。

首先我们给GLSurfaceView设置Renderer渲染器是通过下面这个方法。

mGLSurfaceView.setRenderer(new MyRenderer());

我们进到此方法中,发现它会创建一个GLThread线程并启动

	public void setRenderer(Renderer renderer) {
        ......
        //将renderer赋值给GLSurfaceView的实例变量mRenderer
        mRenderer = renderer;
        mGLThread = new GLThread(mThisWeakRef);
        mGLThread.start();
    }

mThisWeakRef是此GLSurfaceView的一个虚引用,防止内存泄露,它是GLSurfaceView的一个实例变量

private final WeakReference<GLSurfaceView> mThisWeakRef =
            new WeakReference<GLSurfaceView>(this);

GLThread线程的run方法会执行guardedRun()方法

   @Override
   public void run() {
       ......
       try {
           guardedRun();
       } catch (InterruptedException e) {
           // fall thru and exit normally
       } finally {
           sGLThreadManager.threadExiting(this);
       }
   }

guardedRun方法里面首先会创建一个EglHelper实例,它是用来初始化EGL环境配置的,然后启动一个无限循环

private void guardedRun() throws InterruptedException {
    mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
    while (true) {
		......
	}
}

这个无限循环里面东西很多,核心为一下几块:

  • 初始化EGL环境配置,
mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
......
try {
    mEglHelper.start();//
} catch (RuntimeException t) {
    sGLThreadManager.releaseEglContextLocked(this);
    throw t;
}
mHaveEglContext = true;
createEglContext = true;
  • 执行Renderer的onSurfaceCreated方法
//如果EGL环境配置好了,会执行onSurfaceCreated()方法,这个方法实际使用时可能会被多次调用,比如设备被唤醒或从别的Activity切换到此Activity时
if (createEglContext) {
	//mGLSurfaceViewWeakRef就是前面的mThisWeakRef,它是GLThread构造函数里面设置的,通过mGLSurfaceViewWeakRef获取到所指的GLSurfaceView
   GLSurfaceView view = mGLSurfaceViewWeakRef.get();
   if (view != null) {
	   try {
			view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
	   } finally {
			Trace.traceEnd(Trace.TRACE_TAG_VIEW);
	   }
	}
    createEglContext = false;
}
  • 执行Renderer的onSurfaceChanged方法

当Surface的尺寸改变时就会调用onSurfaceChanged()方法

if (sizeChanged) {
    GLSurfaceView view = mGLSurfaceViewWeakRef.get();
    if (view != null) {
	    try {
			view.mRenderer.onSurfaceChanged(gl, w, h);
	    } finally {
			Trace.traceEnd(Trace.TRACE_TAG_VIEW);
	    }
    }
    sizeChanged = false;
}
  • 执行Renderer的onDrawFrame方法

它是在代码块中执行的,也就是说每绘制一帧时都会执行onDrawFrame()方法

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

onDrawFrame()执行之后会交换前后缓冲区。

int swapError = mEglHelper.swap();

public int swap() {
    if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
        return mEgl.eglGetError();
    }
    return EGL10.EGL_SUCCESS;
}
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值