Skip to content

Latest commit

 

History

History
236 lines (151 loc) · 12.5 KB

2.GLSurfaceView简介.md

File metadata and controls

236 lines (151 loc) · 12.5 KB

2.GLSurfaceView简介

生产者(Surface)与消费者(SurfaceFlinger)

在Android系统中,无论开发者调用什么渲染API,一切内容都会渲染到Surface上。

Surface表示缓冲区队列中的生产者,而缓冲区队列会被消费者SurfaceFlinger消耗掉。

在Android平台上创建的每个窗口都由Surface提供支持。 用户通过在Surface这张画布上绘制生成图形数据,这些数据会被BufferQueue传输给SurfaceFlinger,所有被渲染的可见Surface都被SurfaceFlinger合成到屏幕。

Android 通过其框架 API 和原生开发套件 (NDK) 来支持 OpenGL。

Android 框架中有如下两个基本类,用于通过 OpenGL ES API 来创建和操控图形:GLSurfaceViewGLSurfaceView.Renderer。也就是说我们想在Android中使用OpenGL ES的最简单的方法就是将我们要显示的图像渲染到GLSurfaceView上,但它只是一个展示类,至于怎么渲染到上面我们就要自己去实现GLSurfaceView.Renderer来生成我们自己的渲染器从而完成渲染操作,这里是不是感觉Android框架提供的类太少了,怎么没有GLTextureView,后面在分析完GLSurfaceView的源码后,可以直接拷贝自己去实现GLTextureView,有关SurfaceView与TextureView的区别可以看这篇文章

GLSurfaceView(使用OpenGL绘制的图形的视图容器)

SurfaceView在View的基础上创建了独立的Surface,拥有SurfaceHolder来管理它的Surface,渲染的工作可以不在主线程中做。可以通过SurfaceHolder得到Canvas,在单独的线程中,利用Canvas绘制需要显示的内容,然后更新到Surface上。

GLSurfaceView继承自SurfaceView,它主要是在SurfaceView的基础上加入了EGL的管理,并自带了一个GLThread的绘制线程,绘制的工作直接通过OpenGL在绘制线程进行,不会堵塞主线程,绘制的结果输出到SurfaceView所提供的Surface上,这使得GLSurfaceView也拥有了OpenGLES所提供的图形处理能力,通过它定义的Render接口,使更改具体的Render的行为非常灵活,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView既可。也就是说它是对SurfaceView的再次封装,为了方便我们在安卓中使用OpenGL。

GLSurfaceView常用方法

  • setEGLContextClientVersion:设置OpenGL ES的版本,3.0则设置3
  • onPause:暂停渲染,最好在Activity、Fragment的onPause方法内调用,减少不必要的性能开销,避免不必要的崩溃
  • onResume:恢复渲染
  • setRender:设置渲染器
  • setRenderMode:设置渲染模式,有连续渲染和按需渲染两种模式,按需渲染需要调用下面的requestRender方法才会去渲染
  • requestRender:请求渲染,请求异步线程进行渲染,调用后不会立即进行渲染。渲染会回调到Renderer接口的onDrawFrame()方法
  • queueEvent:插入一个Runnable任务到后台渲染线程上执行。相应的,渲染线程中可以通过Activity的runOnUIThread的方法来传递事件给主线程去执行

GLSurfaceView.Renderer(可控制该视图中绘制的图形)

此接口定义了在GLSurfaceView中绘制图形所需的方法。您必须将此接口的一个实现作为单独的类提供,并使用GLSurfaceView.setRenderer()将其附加到您的GLSurfaceView实例。

GLSurfaceView.Renderer接口要求您实现以下方法:

  • onSurfaceCreated():系统会在创建GLSurface时调用一次此方法。使用此方法可执行仅需发生一次的操作,例如设置OpenGL环境参数或初始化OpenGL图形对象。
  • onDrawFrame():完成绘制工作,每一帧图像的渲染都要在这里完成,系统会在每次重新绘制GLSurfaceView时调用此方法。请将此方法作为绘制(和重新绘制)图形对象的主要执行点。
  • onSurfaceChanged():系统会在GLSurfaceView几何图形发生变化(包括GLSurfaceView大小发生变化或设备屏幕方向发生变化)时调用此方法。例如,系统会在设备屏幕方向由纵向变为横向时调用此方法。使用此方法可响应GLSurfaceView容器中的更改。

使用GLSurfaceView和GLSurfaceView.Renderer为OpenGL ES建立容器视图后,您便可以开始使用以下类调用 OpenGL API:

OpenGL ES 3.0/3.1 API 软件包:

  • android.opengl: 此软件包提供了 OpenGL ES 3.0/3.1 类的接口。

GLSurfaceView类

package android.opengl;
/**
 * An implementation of SurfaceView that uses the dedicated surface for
 * displaying OpenGL rendering.
 */
 public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback2 {
 }

GPU加速:GLSurfaceView的效率是SurfaceView的30倍以上,SurfaceView使用画布进行绘制,GLSurfaceView利用GPU加速提高了绘制效率。View的绘制onDraw(Canvas canvas)使用Skia渲染引擎渲染,而GLSurfaceView的渲染器Renderer的onDrawFrame(GL10 gl)使用OpenGL绘制引擎进行渲染。

GLSurfaceView的特性:

  • 管理一个surface,这个surface就是一块特殊的内存,能直接排版到android的视图view上。
  • 管理一个EGL Display,它能让OpenGL把内容渲染到surface上
  • 用户自定义渲染器render
  • 让渲染器在独立的变成里运作(与UI线程分离)
  • 支持按需渲染(on-demand)和连续渲染(continuous)
  • 可以封装、跟踪并且排查渲染器的问题

OpenGL实现一个绿色的activity

这里是全屏渲染成绿色,所以不会涉及到顶点着色器和片段着色器。

首选现在AndroidManifest.xml文件中声明使用OpenGL ES的版本:

<uses-feature android:glEsVersion="0x00030000" android:required="true" />
public class MainActivity extends AppCompatActivity {
    private GLSurfaceView mGlSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGlSurfaceView = new MyGLSurfaceView(this);
        setContentView(mGlSurfaceView);
    }
}

class MyGLSurfaceView extends GLSurfaceView {
    private final MyGLRenderer renderer;
    public MyGLSurfaceView(Context context) {
        super(context);
        // 设置opengl es的版本,现在应该没有app还要支持4.3一下系统了,直接用3.0就可以
        setEGLContextClientVersion(3);
        renderer = new MyGLRenderer();
        setRenderer(renderer);
    }
}

class MyGLRenderer implements GLSurfaceView.Renderer {
    public void onSurfaceCreated(GL10 unused, EGLConfig config) {
        // Set the background frame color RGBA
        GLES30.glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
    }
    public void onDrawFrame(GL10 unused) {
        // Redraw background color
        // glClearColor设置好清除颜色,glClear利用glClearColor设置好的清除颜色来设置颜色缓冲区的颜色
        GLES30.glClear(GLES31.GL_COLOR_BUFFER_BIT);
    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES30.glViewport(0, 0, width, height);
    }
}

效果图就不贴了,就是一个纯绿色的Activity。 上面glClearColor函数是一个状态设置函数,而glClear函数则是一个状态使用的函数,它使用了当前的状态来获取应该清除为的颜色

Render接口重写的三个方法中调用了GLES31的API方法:

  • glClearColor():设置清空屏幕用的颜色,参数为RGBA。
  • glClear():清空屏幕,当调用glClear函数清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。glClear函数来清空屏幕的颜色缓冲时会接受一个缓冲位(Buffer Bit)来指定要清空的缓冲,可能的缓冲位有GL_COLOR_BUFFER_BIT、GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT,由于这里我们只关心颜色值,所以我们只清空颜色缓冲。
  • glViewport():设置视图的尺寸,告诉OpenGL可以用来渲染surface的大小。

RenderMode

GLSurfaceView默认采用的是RENDERMODE_CONTINUOUSLY连续渲染的方式,刷新的帧率是60FPS,16ms就重绘一次,可以通过mGLView.setRenderMode()更改其渲染方式为RENDERMODE_WHEN_DIRTY,表示按需渲染,在surfaceCreate的时候会绘制一次,之后只有在调用requestRender或者onResume等方法主动请求重绘时才会进行渲染,如果你的界面不需要频繁的刷新最好使用RENDERMODE_WHEN_DIRTY,这样可以降低CPU和GPU的活动。

状态处理

使用GLSurfaceView需要注意程序的生命周期,Activity及Fragment会有暂停和恢复等状态,GLSurfaceView也需根据这些状态来做相应的处理,GLSurfaceView具有onResume和onPause两个同Activity及Fragment中的生命周期同名的方法,在Activity或者Fragment中的onResume和onPause方法中,需要主动调用GLSurfaceView的实例的这两个方法,这样能使OpenGLES的内部线程做出正确的判断,从而保证应用程序的稳定性。

GLSurfaceView的事件处理

为了处理事件,需要继承GLSurfaceView类并重载它的事件方法,但是由于GLSurfaceView是多线程的,渲染器在独立的渲染线程中,需要使用Java的跨线程机制与渲染器进行通讯,GLSurfaceView提供了queueEvent(Runnable runnable)方法就是一种相对简单的操作,queueEvent()方法被安全的用于在UI线程和渲染线程之间进行交流通信。这块在注释里面写了:

 * To handle an event you will typically subclass GLSurfaceView and override the
 * appropriate method, just as you would with any other View. However, when handling
 * the event, you may need to communicate with the Renderer object
 * that's running in the rendering thread. You can do this using any
 * standard Java cross-thread communication mechanism. In addition,
 * one relatively easy way to communicate with your renderer is
 * to call
 * {@link #queueEvent(Runnable)}. For example:
 * <pre class="prettyprint">
 * class MyGLSurfaceView extends GLSurfaceView {
 *
 *     private MyRenderer mMyRenderer;
 *
 *     public void start() {
 *         mMyRenderer = ...;
 *         setRenderer(mMyRenderer);
 *     }
 *
 *     public boolean onKeyDown(int keyCode, KeyEvent event) {
 *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
 *             queueEvent(new Runnable() {
 *                 // This method will be called on the rendering
 *                 // thread:
 *                 public void run() {
 *                     mMyRenderer.handleDpadCenter();
 *                 }});
 *             return true;
 *         }
 *         return super.onKeyDown(keyCode, event);
 *     }
 *}

SurfaceTexture

说到GLSurfaceView就一定要提一下SurfaceTexture。

和SurfaceView功能类似,区别是,SurfaceTexure可以不显示在界面中。使用OpenGl对图片流进行美化,添加水印,滤镜这些操作的时候我们都是通过SurfaceTexre去处理,处理完之后再通过GlSurfaceView显示。

缺点,可能会导致个别帧的延迟。本身管理着BufferQueue,所以内存消耗会多一点。

SurfaceTexture从图像流(来自Camera预览,视频解码,GL绘制场景等)中获得帧数据,当调用updateTexImage()时,根据内容流中最近的图像更新SurfaceTexture对应的GL纹理对象,接下来,就可以像操作普通GL纹理一样操作它了。

SurfaceTexture可以将Surface中最近的图像数据更新到GL Texture中。通过GL Texture我们就可以拿到视频帧,然后直接渲染到GLSurfaceView中。

通过 setOnFrameAvailableListener(listener) 可以向 SurfaceTexture 注册监听事件,当Surface有新的图像可用时,调用SurfaceTexture的**updateTexImage()**方法将图像内容更新到GL Texture中,然后做绘制操作。

SurfaceTexture中的attachToGLContext()和detachToGLContext()可以让多个GL context共享同一个内容源。

SurfaceTexture对象可以在任何线程上创建。

updateTexImage()只能在包含纹理对象的OpenGL ES上下文的线程上调用。 在任意线程上调用frame-available回调函数,不与updateTexImage()在同一线程上出现。