Activity的绘制流程(四)

上一篇讲了Activity的绘制流程(三)VSync(https://www.jianshu.com/p/0d9ade638bc8),本篇主讲(四)Surface。
(一)窗口的添加
(二)Choreographer
(三)VSync
(四)Surface
(五)RenderThread
(六)StartingWIndow
(七)窗口切换

一、绘制流程

从前面的介绍中,我们得知了一个Activity对应一个Window,一个Window包含多个View,一个Window又对应一个Surface,Surface相当与一块画布,管理着图形缓冲区。
使用硬件渲染后,主线程的绘制工作主要是采用递归的方式遍历DecorView包含的所有的View,构建和更新一个DisplayList。DisplayList主要包含两个重要的对象,一个是deque队列mChildNodes,View对应一个RenderNode,RenderNode被封装在一个RenderNodeDrawable对象中,并加入到该队列,该队列用于后续同步时取得保存的子RenderNode。可以将这个队列想象成一个树结构来理解,每个节点为RenderNodeDrawable。另一个是DisplayListData对象mDisplayList,该对象中保存了各个Op,一个Op代表一个绘制操作,比如绘制多个点 DrawPoints,比如绘制子View DrawDrawable。
接着主线程会进入睡眠,直到RenderThread线程完成DisplayList的同步工作。然后RenderThread线程会从Surface对应的图形缓冲区队列BufferQueue中申请一块图形缓冲区GraphicBuffer(当然这里的GraphicBuffer只是将物理内存映射到应用的进程空间)进行绘制,简单来说就是将DisplayList中的各个绘制操作填充进去,最后将该GraphicBuffer放回队列中,至此完成了CPU上的绘制流程。

二、Surface

创建Surface.jpg

在Activity的绘制流程(一)窗口的添加中提到了ViewRootImpl的setView函数,在调用Session的addToDisplay函数添加窗口之前,调用了ViewRootImpl的requestLayout函数,该函数往Choreographer的CallbackQueue CALLBACK_TRAVERSAL中添加一个Callback,并且请求VSync。当接收到VSync后,主线程处理CallbackQueue数组中的各个回调,其中CALLBACK_TRAVERSAL会向WMS请求重新布局该窗口,WMS将返回一个SurfaceControl对象给应用,应用端将SurfaceControl对象保存到Surface(https://www.androidos.net.cn/android/10.0.0_r6/xref/frameworks/base/core/java/android/view/Surface.java)中,后续通过该Surface便可以调度到SurfaceFlinger端操作对应的BufferQueue中的GraphicBuffer。

ViewRootImpl.java
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ...
        requestLayout();
        res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                            mTempInsets);
        ...
    }
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ...
        }
    }

创建Surface,其实是在SurfaceFlinger端为Window创建了一个BufferQueue。BufferQueueCore是BufferQueue的实现类,BufferQueueCore初始化的时候并不是triple buffers,而是double buffers。
应用端通过BufferQueueProducer来allocateBuffers(分配GraphicBuffer)、dequeueBuffer(申请GraphicBuffer,映射到应用的进程空间)、queueBuffer(将GraphicBuffer放回BufferQueue)
SurfaceFlinger通过BufferQueueConsumer来acquireBuffer(申请GraphicBuffer)、releaseBuffer(释放GraphicBuffer)
所以BufferQueue、应用端、SurfaceFlinger构成了一个生产者-消费者模型,由应用端绘制Buffer,绘好的Buffer被SurfaceFlinger用于合成。

BufferQueueCore.cpp
    mMaxAcquiredBufferCount(1),
    mMaxDequeuedBufferCount(1),

在调用BufferQueueLayer的onFirstRef函数时,会去判断系统属性ro.sf.disable_triple_buffer的值,默认为0,即设置为triple buffers,可以通过修改系统属性ro.sf.disable_triple_buffer修改为double buffers。

BufferQueueLayer.cpp
    void BufferQueueLayer::onFirstRef() {
        ...
        if (!mFlinger->isLayerTripleBufferingDisabled()) {
            mProducer->setMaxDequeuedBufferCount(2);
        }
        ...
    }

在Android Q中,若帧率大于66.6,即日常见到的90帧、120帧,会将triple buffers改为four buffers。

CanvasContext.cpp
    void CanvasContext::setSurface(sp&& surface) {
        ...
        if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
            mFixedRenderAhead = false;
            mRenderAheadCapacity = 1;
        } else {
            mFixedRenderAhead = true;
            mRenderAheadCapacity = mRenderAheadDepth;
        }
        bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode,mRenderAheadCapacity);
        ...
    }
    
SkiaOpenGLPipeline.cpp
    bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
                                    ColorMode colorMode, uint32_t extraBuffers) {
        ...
        setBufferCount(surface, extraBuffers);
        ...
    }
    static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
        int query_value;
        int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
        auto min_undequeued_buffers = static_cast(query_value);
        int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
        native_window_set_buffer_count(window, bufferCount);
    }

Surface.cpp
    int Surface::setBufferCount(int bufferCount) {
        ...
        err = mGraphicBufferProducer->query(
                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers);
        if (err == NO_ERROR) {
            err = mGraphicBufferProducer->setMaxDequeuedBufferCount(
                    bufferCount - minUndequeuedBuffers);
        }
        ...
    }

三、allocateBuffers

分配Buffer.jpg

GraphicBufferAllocator是Android为上层提供的一个类,用于分配图形缓冲区。图形缓冲区可以从帧缓冲区,也可以从内存中分配,图形缓冲区从帧缓冲区中分配值用于SurfaceFlinger。应用进程申请分配图形缓冲区,由SurfaceFlinger从内存中分配,分配的时候会将物理内存映射到SurfaceFlinger的进程空间,后续当应用端通过BufferQueueProducer来dequeueBuffer函数时才把图形缓冲区映射到应用进程的进程空间。
在BufferQueueCore中有五个重要的列表:
set mFreeSlots(该set中下标的slot表示处于FREE状态,且没有GraphicBuffer)
list mUnusedSlots(该set中下标的slot表示未使用,数目为64 - mFreeSlots)
list mFreeBuffers(该set中下标的slot表示处于FREE状态,但是有GraphicBuffer)
set mActiveBuffers(该set中下标的slot表示处于NO FREE状态,且没有GraphicBuffer)
BufferSlot[] mSlots(保存了所有GraphicBuffer)
在BufferQueueProducer的allocateBuffers函数中,创建一个GraphicBuffer对象。在这里只会创建一个GraphicBuffer,而不是所有Buffer。然后从mFreeSlots取出一个值,mFreeBuffers放入一个值,代表创建了一个Buffer,并将Buffer保存到列表mSlots中。

四、dequeueBuffer

dequeueBuffer.jpg

RenderThread

从systrace中可以看到,dequeueBuffer和queueBuffer都在RenderThread线程上。RenderThread线程申请GraphicBuffer是调用了Surface(非上面提及到的Surface,而是https://www.androidos.net.cn/android/10.0.0_r6/xref/frameworks/native/libs/gui/Surface.cpp)的dequeueBuffer函数,通过Binder通信调用到SurfaceFlinger端申请GraphicBuffer。在allocateBuffers中说了创建了一个GraphicBuffer,所以若是在列表mFreeBuffers中申请不到GraphicBuffer且已经申请的GraphicBuffer数目没有超过阈值,则创建一个GraphicBuffer对象。得到GraphicBuffer后,将其映射到应用进程的进程空间。

五、queueBuffer

queueBuffer.jpg

将DisplayList中的各个绘制操作填充到GraphicBuffer,RenderThread线程通过调用Surface的queueBuffer函数binder通信SurfaceFlinger端,将该GraphicBuffer放回到对应的队列中,至此完成了CPU上的绘制流程。但是并不代表这一帧已经绘制完成,该帧的绘制需要GPU的参与,而CPU不等带GPU绘制完成,是为了让CPU和GPU能够并行操作,减少耗时。

六、Fence

CPU和GPU并行操作GraphicBuffer,必然会牵扯到同步的问题,即CPU如何得知GPU已经完成一帧的绘制,从而引入了Fence。
在queueBuffer时,会给GraphicBuffer添加一个Fence,当SurfaceFlinger端调用BufferQueueConsumer的acquireBuffer函数获取GraphicBuffer用于合成时,若GPU还没有执行到这条Fence指令,则会等待,直到GPU执行到Fence指令,才会从等待中被唤醒。
Fence有sync_pt、sync_timeline和sync_fence三个概念:

  1. sync_timeline:时间轴。每个流程必须有一个时间轴,时间轴是一个单调递增的计数器,时间轴由时间值组成,时间轴上可以指定有许多同步点,当时间轴当前递增超过该同步点,则同步点状态切换。
  2. sync_pt:同步点,代表时间轴上一个特殊的值,有三种状态:active(刚创建,此时一般时间轴都是小于pt)、signal(时间轴增大到同步点)、error。
  3. sync_fence:一系列同步点的集合,每个同步点只能属于一个Fence,一个Fence包含的同步点可以来自不同的时间轴,Fence可以合并,当Fence中的所有同步点被唤醒后,Fence状态切换。

你可能感兴趣的:(Activity的绘制流程(四))