图形系统

Q:SurfaceFlinger是什么?有什么用?

image SurfaceFlinger负责合成所有的Layer并送显到Display,在Android系统中,SurfaceFlinger是一个独立进程

Q:什么是Surface?

Surface是一个窗口,例如:一个Activity是一个Surface、一个Dialog也是一个Surface,承载了上层的图形数据,与SurfaceFlinger侧的Layer相对应

Q:Choreographer是什么?有什么用?

在收到VSync pulse后,将马上开始下一帧的渲染。即一旦收到VSync通知,CPU和GPU就立刻开始计算然后把数据写入buffer。而这一"drawing with VSync" 的实现就是Choreographer

Q:Choreographer是如何控制实现在下一个Vsync信号到来时实现View绘制的?

  • 所有UI的变化都是走到ViewRootImpl的scheduleTraversals()方法。
  • 在VSync信号到来时才会执行绘制,即ViewRootImpl.performTraversals()

Q:ViewRootImpl是如何实现从scheduleTraversals到performTraversals的?

  1. 首先使用mTraversalScheduled字段保证同时间多次更改只会刷新一次,例如TextView连续两次setText(),也只会走一次绘制流程。
  2. 然后把当前线程的消息队列Queue添加了同步屏障,这样就屏蔽了正常的同步消息,保证VSync到来后立即执行绘制,而不是要等前面的同步消息。后面会具体分析同步屏障和异步消息的代码逻辑。
  3. 调用了mChoreographer.postCallback()方法,发送一个会在下一帧执行的回调,即在下一个VSync到来时会执行TraversalRunnable–>doTraversal()—>performTraversals()–>绘制流程。

Q:Choreograhper是如何触发view绘制的?

image

Q:Choreographer单例是怎么实现的?保存在哪里?

  • 在ViewRootImpl构造方法中获取了Choreographer实例
  • 保持在了自己单例的ThreadLocal中

Q:ChoreoGrapher在初始化的时候,都做了什么?

  • 创建了一个mHandler,FrameHandler:传入当前线程Looper,用来分发每一帧事件,主要有三种:有延迟的任务发延迟消息、不在原线程的发到原线程、没开启VSYNC的直接走 doFrame 方法取执行绘制
  • VSync事件接收器mDisplayEventReceiver,继承runnable:在onVsync回调中将本身将接收器本身作为runnable传入异步消息msg,并使用mHandler发送msg,最终执行的就是doFrame()方法了,这里因为是使用handler发送消息到MessageQueue中,不一定是立刻执行,如何MessageQueue中前面有较为耗时的操作,那么就要等完成,才会执行本次的doFrame()
  • 任务链表数组mCallbackQueues:建一个链表类型CallbackQueue的数组,大小为5,也就是数组中有五个链表,每个链表存相同类型的任务,按照处理优先级依次为:输入、动画、遍历绘制等任务(CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL)

Q:一些QA

  1. 丢帧(掉帧) ,是说 这一帧延迟显示 还是丢弃不再显示 ? 答:延迟显示,因为缓存交换的时机只能等下一个VSync了。
  2. 布局层级较多/主线程耗时 是如何造成 丢帧的呢? 答:布局层级较多/主线程耗时 会影响CPU/GPU的执行时间,大于16.6ms时只能等下一个VSync了。
  3. 16.6ms刷新一次 是啥意思?是每16.6ms都走一次 measure/layout/draw ? 答:屏幕的固定刷新频率是60Hz,即16.6ms。不是每16.6ms都走一次 measure/layout/draw,而是有绘制任务才会走,并且绘制时间间隔是取决于布局复杂度及主线程耗时。
  4. measure/layout/draw 走完,界面就立刻刷新了吗? 答:不是。measure/layout/draw 走完后 会在VSync到来时进行缓存交换和刷新。
  5. 如果界面没动静止了,还会刷新吗? 答:屏幕会固定没16.6ms刷新,但CPU/GPU不走绘制流程。见下面的SysTrace图。
  6. 可能你知道VSYNC,这个具体指啥?在屏幕刷新中如何工作的? 答:当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时会出现的vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。并且Android4.1后 CPU/GPU的绘制是在VSYNC到来时开始。
  7. 可能你还听过屏幕刷新使用 双缓存、三缓存,这又是啥意思呢? 答:双缓存是Back buffer、Frame buffer,用于解决画面撕裂。三缓存增加一个Back buffer,用于减少Jank。
  8. 可能你还听过神秘的Choreographer,这又是干啥的? 答:用于实现——“CPU/GPU的绘制是在VSYNC到来时开始”

Q:HWC是什么?有什么用?

HWC(hwcomposer)是Android中进行窗口(Layer)合成和显示的HAL层模块,其实现是特定于设备的,而且通常由显示设备制造商 (OEM)完成,为SurfaceFlinger服务提供硬件支持

Q:Vsync信号是什么?

VSync信号是由HWC硬件模块根据屏幕刷新率产生

Q:客户端是如何请求Vsync信号的?是合适触发的?

image

  1. 当View.invalidate调用后,最终会触发ViewRootImpl向Choreographer注册一个TraversalRunnable。
  2. Choreographer本地保存这个TraversalRunnable后,会通过DisplayEventReceiver.java调用到Native层,最终一步步调用到mEventThread线程,修改connection->count = 0(请求接收下一次VSync)。
  3. Vsync到来后,会从SurfaceFlinger进程一步步回调到客户端进程,最终触发ViewRootImpl之前注册TraversalRunnable,启动View树的渲染。

Q:说说Android的多重缓冲?

双缓存的交换 是在Vsyn到来时进行,交换后屏幕会取Frame buffer内的新数据,而实际 此时的Back buffer 就可以供GPU准备下一帧数据了。 如果 Vsyn到来时 CPU/GPU就开始操作的话,是有完整的16.6ms的,这样应该会基本避免jank的出现了,Google在Android 4.1系统中对Android Display系统进行了重构,实现了Project Butter(黄油工程):系统在收到VSync pulse后,将马上开始下一帧的渲染。即一旦收到VSync通知(16ms触发一次),CPU和GPU 才立刻开始计算然后把数据写入buffer

Q:如何设计一个双缓冲的view显示?

        /* 装载资源 */
        mBitQQ = ((BitmapDrawable) getResources().getDrawable(R.drawable.qq)).getBitmap();
        /* 创建屏幕大小的缓冲区 */
        mSCBitmap = Bitmap.createBitmap(320, 480, Config.ARGB_8888);
        /* 创建Canvas */
        backCanvas = new Canvas();
        /* 设置将内容绘制在mSCBitmap上 */
        backCanvas.setBitmap(mSCBitmap);
        mPaint = new Paint();
        /* 将mBitQQ绘制到mSCBitmap上 */
        backCanvas.drawBitmap(mBitQQ, 0, 0, mPaint);

    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        /* 将mSCBitmap显示到屏幕上 */
        canvas.drawBitmap(mSCBitmap, 0, 0, mPaint);
    }

Q:SurfaceTexture,TextureView, SurfaceView和GLSurfaceView?

SurfaceView:

SurfaceView从Android 1.0(API level 1)时就有 。它继承自类View,因此它本质上是一个View。但与普通View不同的是,它有自己的Surface

  • 可以放到单独线程去做,渲染时可以有自己的GL context,不会影响主线程对事件的响应
  • 因为Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换
  • SurfaceView是放在其他最底层的视图层次中,所有其他视图层都在它上面,所以在它之上可以添加一些层,而且它不能是透明的
  • 可以控制显示的fps
//用于显示的surfaceview
public class SurfaceAnimatorView extends SurfaceView implements SurfaceHolder.Callback {

    private IAnimator iAnimator;
    private Thread mRenderThread;
    private SurfaceHolder surfaceHolder;
    private Paint mPaintClear;
    // 控制帧率
    private int fps = 1000 / 35; 
    private volatile boolean isStop = false;
    // 负责不断绘制的runnable,在子线程执行
    private Runnable mRenderRunnable = new Runnable() {
        @Override
        public void run() {
            while (!isStop) {
                long start = System.currentTimeMillis();
                onDrawAnimator();
                long spendTime = System.currentTimeMillis() - start;
                try {
                    if ((fps - spendTime) > 0) {
                        Thread.sleep(fps - spendTime);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    private void onDrawAnimator() {

        // 从surfaceHolder 获取离屏的canvas 
        Canvas canvas = surfaceHolder.lockCanvas();
        if (canvas == null) {
            return;
        }
        // 清屏。这步很重要,不然画布会有上次绘制的内容
        canvas.drawPaint(mPaintClear);
        // 将画布给animator,实现对应的动画
        iAnimator.onDraw(canvas);
        // 释放canvas
        surfaceHolder.unlockCanvasAndPost(canvas);
    }

    public SurfaceAnimatorView(Context context) {
        super(context);
        init();
    }

    public SurfaceAnimatorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public void init() {
        setFocusable(true);
        if (surfaceHolder == null) {
            surfaceHolder = getHolder();
            surfaceHolder.addCallback(this);
        }
        this.setZOrderOnTop(true);
        this.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        mPaintClear = new Paint();
        mPaintClear.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        setBackgroundColor(getContext().getResources().getColor(R.color.teal_200));
    }


    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        // 初始化animator 
        iAnimator = new CircleLoadingAnimator(getContext());
        iAnimator.onLayout(getWidth(), getHeight());
        isStop = false;
        // 启动绘制的线程
        mRenderThread = new Thread(mRenderRunnable);
        mRenderThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        isStop = true;
        try {
            mRenderThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//动画接口
public interface IAnimator {
    void onLayout(int width, int height);

    void onDraw(Canvas canvas);
}

// 具体实现动画的类
public class CircleLoadingAnimator implements IAnimator {
    private DecelerateInterpolator fastToSlow = new DecelerateInterpolator();

    // 每帧变化的幅度,越小越慢,帧区别也越小
    private int INCREASE_VALUE = 4;
    private int MAX_CIRCLE_RADIUS = 200;
    private int CIRCLE_RADIUS = MAX_CIRCLE_RADIUS >> 1;
    private int mSmallCircleRadius = 0;
    private int mBigCircleRadius = MAX_CIRCLE_RADIUS;
    private int increaseValue = -1;
    private int recordValue = MAX_CIRCLE_RADIUS;
    private Paint mSmallPaint = new Paint();
    private Paint mBigPaint = new Paint();
    private int mX;
    private int mY;
    private static final String TAG = "CircleLoadingAnimator";

    public CircleLoadingAnimator(Context context) {
        mBigPaint.setStyle(Paint.Style.FILL);
        mBigPaint.setStrokeCap(Paint.Cap.ROUND);
        mBigPaint.setAntiAlias(true);
        mBigPaint.setColor(context.getResources().getColor(R.color.white_90));

        mSmallPaint.setStyle(Paint.Style.FILL);
        mSmallPaint.setStrokeCap(Paint.Cap.ROUND);
        mSmallPaint.setAntiAlias(true);
        mSmallPaint.setColor(context.getResources().getColor(R.color.white));
    }

    @Override
    public void onLayout(int width, int height) {
        mX = width >> 1;
        mY = height >> 1;
    }

    @Override
    public void onDraw(Canvas canvas) {
        updateInCreaseValue();
        recordValue += increaseValue;
        // 模拟属性动画 0 - > 1 的过程
        float value = (float) ((MAX_CIRCLE_RADIUS - recordValue) * 1.0 / (CIRCLE_RADIUS));
        // 更新圆半径
        mBigCircleRadius = (int) (fastToSlow.getInterpolation(1 - value) * CIRCLE_RADIUS + CIRCLE_RADIUS);
        mSmallCircleRadius = (int) (CIRCLE_RADIUS - fastToSlow.getInterpolation(1 - value) * CIRCLE_RADIUS);
        // 画圆
        canvas.drawCircle(mX, mY, mSmallCircleRadius, mSmallPaint);
        canvas.drawCircle(mX, mY, mBigCircleRadius, mBigPaint);
    }

    // 更新边界值
    private void updateInCreaseValue() {
        if (mBigCircleRadius >= MAX_CIRCLE_RADIUS) {
            increaseValue = -1 * INCREASE_VALUE;
        } else if (mBigCircleRadius <= (CIRCLE_RADIUS)) {
            increaseValue = INCREASE_VALUE;
        }
    }
}

GLSurfaceView:

GLSurfaceView从Android 1.5(API level 3)开始加入,作为SurfaceView的补充。它可以看作是SurfaceView的一种典型使用模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程

SurfaceTexture:

和SurfaceView不同的是,它对图像流的处理并不直接显示,而是转为GL外部纹理,因此可用于图像流数据的二次处理(如Camera滤镜,桌面特效等

  • 它核心管理着一个BufferQueue的Consumer和Producer两端。Producer端用于内容流的源输出数据,Consumer端用于拿GraphicBuffer并生成纹理
  • SurfaceTexture.OnFrameAvailableListener用于让SurfaceTexture的使用者知道有新数据到来

TextureView:

TextureView 在4.0(API level 14)中引入。它可以将内容流直接投影到View中,可以用于实现Live preview等功能

  • 它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化
  • 必须在硬件加速的窗口中。它显示的内容流数据可以来自App进程或是远端进程
SurfaceTexture mOESSurfaceTexture;
TextureView mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(mTextureListener);//设置Texture作为展示界面

public TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            mOESTextureId = Utils.createOESTextureObject();
            mEglSurface = mEgl.eglCreateWindowSurface(mEGLDisplay, mEGLConfig[0], surfaceTexture, null);//创建openGL显示窗口
            mOESSurfaceTexture = new SurfaceTexture(mOESTextureId);
            mOESSurfaceTexture.setOnFrameAvailableListener(mFrameLsn);//初始化SurfaceTexture并绑定FrameAvailable回调

            mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            DisplayMetrics dm = new DisplayMetrics();
            mCamera = new CameraV1(CameraV1TextureViewActivity.this);
            if (!mCamera.openCamera(dm.widthPixels, dm.heightPixels, mCameraId)) {
                return;
            }

            mCamera.setPreviewTexture(mOESSurfaceTexture);
            mCamera.startPreview();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.releaseCamera();
                mCamera = null;
            }
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        }
    };

public SurfaceTexture.OnFrameAvailableListener mFrameLsn = new SurfaceTexture.OnFrameAvailableListener(){
    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        long t1, t2;
        t1 = System.currentTimeMillis();
        if (mOESSurfaceTexture != null) {
            mOESSurfaceTexture.updateTexImage();
            mOESSurfaceTexture.getTransformMatrix(transformMatrix);
        }
        mEgl.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext);
        GLES20.glViewport(0,0,mTextureView.getWidth(),mTextureView.getHeight());
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f, 1f, 0f, 0f);
        aPositionLocation = glGetAttribLocation(mShaderProgram, FilterEngine.POSITION_ATTRIBUTE);
        aTextureCoordLocation = glGetAttribLocation(mShaderProgram, FilterEngine.TEXTURE_COORD_ATTRIBUTE);
        uTextureMatrixLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_MATRIX_UNIFORM);
        uTextureSamplerLocation = glGetUniformLocation(mShaderProgram, FilterEngine.TEXTURE_SAMPLER_UNIFORM);

        //绘制图像到openGL缓存中
        glActiveTexture(GLES20.GL_TEXTURE0);
        glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);
        glUniform1i(uTextureSamplerLocation, 0);
        glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0);

        if (mBuffer != null) {
            mBuffer.position(0);
            glEnableVertexAttribArray(aPositionLocation);
            glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 16, mBuffer);

            mBuffer.position(2);
            glEnableVertexAttribArray(aTextureCoordLocation);
            glVertexAttribPointer(aTextureCoordLocation, 2, GL_FLOAT, false, 16, mBuffer);

            glDrawArrays(GL_TRIANGLES, 0, 6);
        }
        //交换显示内存
        mEgl.eglSwapBuffers(mEGLDisplay, mEglSurface);
        t2 = System.currentTimeMillis();
        Log.i(TAG, "drawFrame: time = " + (t2 - t1));
    }
};