【阅读笔记】Surface系统

参考:
【Android6.0 显示系统(一) Surface创建】
https://blog.csdn.net/kc58236582/article/details/52670528
【Android6.0 显示系统(二) SurfaceFlinger创建Surface】
https://blog.csdn.net/kc58236582/article/details/52680288
【Android6.0 显示系统(三) 管理图像缓冲区】
https://blog.csdn.net/kc58236582/article/details/52681363
【Android6.0 显示系统(四) 图像显示相关】
https://blog.csdn.net/kc58236582/article/details/52690130
【Android6.0 显示系统(五) SurfaceFlinger服务】
https://blog.csdn.net/kc58236582/article/details/52763534
【Android6.0 显示系统(六) 图像的输出过程】
https://blog.csdn.net/kc58236582/article/details/52778333

OpenGrok地址http://androidxref.com/

注:本文部分描述直接摘抄原文。


1 Surface的创建

1.1 应用层创建Surface
1.2 WMS创建Surface

1.1 应用层创建Surface

一般Activity中的Surface是ViewRootImpl中通过WMS创建的。当然也会有需要在应用中独立创建Surface的情况,比如相机,视频播放器等应用。它们是通过SurfaceView来使用surface.

  • SurfaceVIew中---> new Surface()
image.png
  • surface的updatewindow函数
    【SurfaceView】updateWindow ---> 【SurfaceView】mSession.relayout()
int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility,
            int flags, out Rect outFrame, out Rect outOverscanInsets,
            out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
            out Rect outOutsets, out Configuration outConfig, out Surface outSurface);

mSession, 是一个IWindowSession, 用于和WMS通信, 在调用relayout函数的时候通过outSurface从WMS获得Surface对象, 这里的outSurface就是传入的mNewSurface。返回是通过Parcel的方式

  • Surface的获取流程
    【Surface】readFrameParcel——> 【Surface】nativeReadFromParcel(在native层创建一个surface对象) ----->【Surface】setNativeObjectLocked(将native层创建的对象保存在java的本地对象中)

在调用nativeReadFromParcel在native层创建surface对象的时候会从binder中读取进程间传输的binder,并将读取的binder对象作为参数创建一个IGraphicBufferProducer对象,然后在以该对象创建Surface

小结,在应用层使用surface最终也是在native层创建一个surface对象,并且对应着一个IGraphicBufferProducer

image.png

1.2 WMS层创建Surface

在应用层通过relayout调用后通过binder的通信最终会调用到WMS.relayout,这个时候就开始进行WMS层的Surface创建过程了。

  • 【WMS】relayoutWindow --->【WMS】 winAnimator.createSurfaceLocked(获得SurfaceControl对象)---.>outSurface.copyFrom ---> 把native层的surfacecontrol对象中的surface复制到Surface的mNative中。

  • SurfaceControl的创建
    【WMS】winAnimator.createSurfaceLocked---> 【SurfaceControl】nativeCreate(创建一个native层的surfacecontrol对象)

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
    sp<SurfaceControl> surface = client->createSurface(
            String8(name.c_str()), w, h, format, flags);
    if (surface == NULL) {
        jniThrowException(env, OutOfResourcesException, NULL);
        return 0;
    }
    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());

来看android_view_SurfaceSession_getClient就是获取SurfaceSession的mNativeObject对象,也就是SurfaceComposerClient对象。

这里出现了几个对象:
IWindowSession: 跨进程通信的接口,供客户端使用,服务端实现在Session
Session:对IWindowSession的实现,并封装了WMS的实例,可以和WMS交互;同时在addToDisplay的时候把客户端的IWindow对象传递给了WMS,这样WMS就可以管理客户端的Window。
SurfaceSession:java层的对象,对应了natvie层的SurfaceComposerClient
SurfaceComposerClient: 在底层和Surface可以建立通信。

  • SurfaceSession的创建
    在ViewRootImpl的setView时候,会调用mWindowSession(IWindowSession-->实现在Session.java中)的addToDisplay函数,在这个函数调用了WMS的addWindow函数,而在addWindow方法,后面会创建一个WindowState对象,然后调用了其attach方法。

【WindowState】attach--->【Session】windowAddedLocked --->【Session】new SurfaceSession()【创建SurfaceSession,同时让wms记录该条surfacesession】---> 【SurfaceSession】nativeCreate
----> 【android_view_SurfaceSession】nativeCreate(在native层创建SurfaceComposerClient对象,该对象可以连接到SurfaceFlinger)

  • SurfaceComposerClient连接SurfaceFlinger
    负责连接到SurfaceFlinger,并获得SurfaceFinger端为该客户端创建的client对象,保存在SurfaceComposerClient中的mClient对象中。当需要创建Surface的时候会调用到SurfaceFlinger中去。
    比如在创建Surface的时候, 是通过创建java层的SurfaceControl ---> 获得native层的SurfaceComposerClient对象--->通过SurfaceComposerClient对象来中和Surfaceflinger连接的mClient对象调用到SurfaceFlinger ---> SurfaceFlinger创建gbp(graphicBufferProducer,handle)对象,然后封装到native层的SurfaceControl中,返回给客户端java层,因此后续通过java层的SurfaceControl对象就可以通过JNI层获得native层的SurfaceControl对象,并获得SurfaceFlinger端创建的gbp
WMS获取Surface.png

**涉及到的类:SurfaceView, SurfaceControl, SurfaceComposerClient,SurfaceControl, **



2 SurfaceFlinger创建Surface

2.1 创建Layer
2.2 获取gbp

在第一部分中已知从java层调用到了SurfaceFlinger层创建Surface,现在就从SurfaceFlinger的createSurface入手。

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle, //唯一标识, IBinder
        sp<IGraphicBufferProducer>* gbp) //图像缓冲对象
{
    /*
     * createSurface must be called from the GL thread so that it can
     * have access to the GL context.
     */

    class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
        sp<IBinder>* handle;
        sp<IGraphicBufferProducer>* gbp;
        status_t result;
        const String8& name;
        uint32_t w, h;
        PixelFormat format;
        uint32_t flags;
    public:
        MessageCreateLayer(SurfaceFlinger* flinger,
                const String8& name, Client* client,
                uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
                sp<IBinder>* handle,
                sp<IGraphicBufferProducer>* gbp)
            : flinger(flinger), client(client),
              handle(handle), gbp(gbp), result(NO_ERROR),
              name(name), w(w), h(h), format(format), flags(flags) {
        }
        status_t getResult() const { return result; }
        virtual bool handler() {
            result = flinger->createLayer(name, client, w, h, format, flags,
                    handle, gbp);
            return true;
        }
    };

    sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle, gbp);
    mFlinger->postMessageSync(msg);
    return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}

这个函数中定义了一个消息类MessageCreateLayer,然后把它的对象通过postMessageSync方法发送出去,这个消息是以同步的方式发送,因此函数结束后可以直接返回结果。因此就直接到了handler函数,在这个函数中调用了SurfaceFlinger的createLayer函数

2.1 创建Layer

【SurfaceFlinger】createLayer ---> 创建不同的Layer ---> 然后从layer中获得handle, gbp.

2.1 获取handle

下面我们再来看handle的获取,只是新建一个Handle,而这个Handle只是一个Binder的实现,就是标识Surface的全局唯一性,没有什么实际的内容。

/*
 * The layer handle is just a BBinder object passed to the client
 * (remote process) -- we don't keep any reference on our side such that
 * the dtor is called when the remote side let go of its reference.
 *
 * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
 * this layer when the handle is destroyed.
 */
class Layer::Handle : public BBinder, public LayerCleaner {
    public:
        Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
            : LayerCleaner(flinger, layer), owner(layer) {}

        wp<Layer> owner;
};

sp<IBinder> Layer::getHandle() {
    Mutex::Autolock _l(mLock);

    LOG_ALWAYS_FATAL_IF(mHasSurface,
            "Layer::getHandle() has already been called");

    mHasSurface = true;

    return new Handle(mFlinger, this);
}

2.2 获取gbp


image.png

在这里是直接返回对象mProducer。


image.png

这里出现了三个新的类:
BufferQueue, 队列,连接producer和consumer的核心。
MonitoredProducer, 只是一个代理类,真正工作的是参数producer, 表示生产者。
SurfaceFlingerConsumer, 消费者代理方,surflinger消耗方。

image.png

在BufferQueue创建生产者和消费者的具体实现,是创建了三个对象:BufferQueueCore, BufferqueueProducer, BufferQueueConsumer , 这三个对象非常重要。

image.png


3 管理图像缓冲区

3.1 生产者和core的联系
3.2 消费者和core联系
3.3 三者联系
3.4 GraphicBuffer对象的创建
3.5 内存缓冲区的fd传递到客户进程

  • 最后分析到MonitoredProducer对象,这个对象只是一个代理,真正实是BufferQueueProducer类,这个对象和BufferQueueCore有关联,可以管理最多达64块的缓冲区。Surface可以理解为一张画布,那么Surface为何要和一个缓冲区队列相关呢?在播放动画时,美妙至少要播放24帧画面才能形成比较真实的动画效果。而这些数据是通过cpu解码得到的,准备他们需要时间。对于图像显示设备而言,刷新周期是固定的,我们必须要在它需要数据的时候把数据准备好。视频播放的每一帧也需要在指定的时间播放,因此解码器会提前准备好一批数据,这些数据保存在解码器内存的缓冲区中,当时间到达是,解码器会把内部缓冲区的图像复制到Surface中,但是显示设备并不是立刻就把数据取走的,因此Surface也需要缓冲区来临时保存数据。

3.1 生产者和core的联系

BufferQueueProducer是IGraphicBufferProducer的实现类,IGraphicBufferProducer中定义了使用buffer的一些方法接口。
一般在使用这个buffer之前会先connect, 在使用结束后会disconnect

在BufferQueueCore类中定义了一个64项的数据mSlots。

 BufferQueueDefs::SlotsType mSlots;

每个缓冲区的类型是BufferSlot类型。它有两个重要的成员变量,mGraphicBuffer是指向图像缓冲区GraphicBuffer的指针,mBufferState表示图像缓冲区的状态。

sp<GraphicBuffer> mGraphicBuffer;
    ......
    BufferState mBufferState;

BufferState的状态有下面几个

enum BufferState {
        FREE = 0,//空闲
        DEQUEUED = 1,//生产状态,被生产者拥有
        QUEUED = 2,//保存数据状态,被BufferQueue拥有
        ACQUIRED = 3//消费状态,被消费者拥有
    };

BufferQueueProducer的dequeueBuffer函数用来向BufferQueueCore申请一个空闲的slot这个slot可能已经有缓冲区,也可能没有如果没有缓冲区,dequeueBuffer函数会分配一块新的缓冲区。得到空闲的slot后,还?需要调用requestBuffer函数来取得一块缓冲区??。得到缓冲区,如果不需要了,可以使用cancelBuffer函数来释放这个slot。调用dequeueBuffer函数之后,缓冲区的拥有者是生产者,缓冲区处于DEQUEUED状态。一旦缓冲区复制数据完成,通过queueBuffer函数把缓冲区的控制权交还给BufferQueueCore,这时候缓冲区将处于QUEUED状态

3.2 消费者和core联系

IGraphicBufferConsumer定义接口,实际实现类是BufferQueueConsumer:

virtual status_t acquireBuffer(BufferItem* outBuffer,
            nsecs_t expectedPresent, uint64_t maxFrameNumber = 0) override;
    ......
 
    virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
            const sp<Fence>& releaseFence, EGLDisplay display,
            EGLSyncKHR fence);
 
 
    virtual status_t connect(const sp<IConsumerListener>& consumerListener,
            bool controlledByApp);
    virtual status_t disconnect()

BufferQueueConsumer类是接口IGraphicBufferComsumer的实现,在使用它之前,先要调用connect函数建立联系,这里传递的参数是IConsumerListener对象,是一个回调接口,如果BufferQueue中有数据准备好了就会调用它的onFrameAvailable函数来通知消费者取走数据。
取走数据的时候,需要调用acquireBuffer函数,将缓冲区状态变成ACQUIRED,使用完之后调用releaseBuffer函数可以吧缓冲区数据归还给BufferQueueCore,这样缓冲区就变成FREE

3.3 三者联系

对象BufferQueueProducer和BufferQueueConsumer好像没有直接联系,其实都是通过共同的BufferQueueCore对象连接在一起的,很多操作时直接使用BufferQueueCore对象的成员变量而不是函数来完成的。

3.4 GraphicBuffer对象的创建

dequeueBuffer函数的部分代码,在从BufferQueueCore中获取到slot的时候,如果需要重新分配图像缓冲区就会调用mCore->mAllocator->createGraphicBuffer函数来重新创建一个图像缓冲区。

        ......
        *outSlot = found;//found复制到outslot
        ATRACE_BUFFER_INDEX(found);
 
        attachedByConsumer = mSlots[found].mAttachedByConsumer;
 
        mSlots[found].mBufferState = BufferSlot::DEQUEUED;//slot的状态修改变成生产状态
 
        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
        if ((buffer == NULL) ||//为空,或者需要重新分配
                buffer->needsReallocation(width, height, format, usage))
        {
            mSlots[found].mAcquireCalled = false;
            mSlots[found].mGraphicBuffer = NULL;
            mSlots[found].mRequestBufferCalled = false;
            mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
            mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
            mSlots[found].mFence = Fence::NO_FENCE;
            mCore->mBufferAge = 0;
 
            returnFlags |= BUFFER_NEEDS_REALLOCATION;//需要重启分配缓冲区
        } else {
            // We add 1 because that will be the frame number when this buffer
            // is queued
            mCore->mBufferAge =
                    mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
        }
 
        BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64,
                mCore->mBufferAge);
 
        if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
            BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
                    "slot=%d w=%d h=%d format=%u",
                    found, buffer->width, buffer->height, buffer->format);
        }
 
        eglDisplay = mSlots[found].mEglDisplay;
        eglFence = mSlots[found].mEglFence;
        *outFence = mSlots[found].mFence;
        mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
        mSlots[found].mFence = Fence::NO_FENCE;
 
        mCore->validateConsistencyLocked();
    } // Autolock scope
 
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {//如果需要重启分配图像缓冲区
        status_t error;
        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
        sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(//创建图像缓冲区
                width, height, format, usage, &error));
        if (graphicBuffer == NULL) {
            BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
            return error;
        }
 
        { // Autolock scope
            Mutex::Autolock lock(mCore->mMutex);
 
            if (mCore->mIsAbandoned) {
                BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                return NO_INIT;
            }
 
            graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
            mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
        } // Autolock scope
    }
    ......

其中(mCore->mAllocator->createGraphicBuffer是调用到了SurfaceFlinger中创建的GraphicBufferAlloc类中,通过GraphicBufferAlloc分配缓冲。

sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,
        uint32_t height, PixelFormat format, uint32_t usage, status_t* error) {
    sp<GraphicBuffer> graphicBuffer(
            new GraphicBuffer(width, height, format, usage));
    status_t err = graphicBuffer->initCheck();
    *error = err;
    ......//错误处理
    return graphicBuffer;
}

创建一个GraphicBuffer, 在这里创建了一个graphicBuffer,在GraphicBuffer的构造函数中会通过GraphicBufferAllocator进行实际内存的分配。

GraphicBufferAllocator实际是装载了Gralloc模块,实际通过Gralloc模块进行分配,这个模块是在GraphicBufferAllocator的构造函数中装载。
调用alloc分配了一块共享的内存缓冲区,alloc函数将返回共享区的fd和缓冲区的指针。既然GraphicBuffer中的缓冲区是共享内存,我们知道使用共享内存需要传递共享内存的句柄fd。下面我们看看是如何传到客户进程的。

3.5 内存缓冲区的fd传递到客户进程(暂时略过)

通过序列化的方式传递
GraphicBuffer类从模板类Flattenable派生,这个派生类可以通过Parcel传递,通常派生类需要重载flatten和unflatten方法,用于对象的序列化和反序列化。

class GraphicBuffer
    : public ANativeObjectBase< ANativeWindowBuffer, GraphicBuffer, RefBase >,
      public Flattenable<GraphicBuffer>

通过flatten函数,中fds用来传递文件句柄,函数把handle中的句柄复制到fds中,然后这些句柄就可以在通过binder传递到目标进程中去。

然后在unflatten函数调用时候,共享文件句柄已经准备好了,但是内存还没有进行映射;调用mBufferMapper.registerBuffer函数进行内存映射,实际调用到了Gralloc模块的gralloc_register_buffer函数,这个函数就调用了mmap进行共享内存映射。

在硬件设备支持Framebuffer缓冲区的情况下,Surface中绘制图形的缓冲区就是Framebuffer的缓冲区,绘制完成后,如果不需要进行图像合成,只需要flip操作就能完成图像的输出,中间完全不用复制的过程,很高效。

image.png

小结: 这部分作者kc专栏主要讲解了在surface端申请的缓冲是如何管理的。以及graphicbuffer的分配和传递方式。



4 图像显示相关内容

概念:刷新率,帧率,帧缓冲(framebuffer),双缓冲技术,Vsync信号,三重缓冲技术。
VSync是一个硬件信号,一般是显示器刷新周期到了会发送。
HWComposer: 表示显示的硬件设备。

HwComposer----> loadFbHalModule(装载Framebuffer的硬件模块) -----> loadHwcModule(装载HWComposer的硬件模块) ---->根据方式不同产生不同的vsync来源(软件模拟或硬件产生)


image.png

4.1 Framebuffer原理

这里的讲解流程比较简单,主要是通过gralloc模块打开kernel提供过的fb节点。直接根据kc专栏的分析画个流程图便于自己今后快速查看。

image.png

分配缓冲图像缓冲区内存: 最终实现是通过galloc模块的gralloc_alloc
小结:这节主要清楚图像是怎么显示,什么是framebuffer,双缓冲,以及vsync即可。



5 SurfaceFlinger

surfaceflinger服务是一个native服务,是图像系统非常重要的一个服务,负责图层的合成,vsync的分发,以及和hal层的交互到图像的渲染。
surfaceflinger怎么启动的就不用多看(通过.rc配置在Init阶段启动,很多native层服务都是,native层的守护进程同样如此比如vold,healthd,netd等)。启动过后surfaceflinger干了什么事情是需要重点关注。

5.1 服务启动

在启动的时候调用到了main_surfaceflinger.cpp中的main函数,在这里干了几件事:

  • 配置binder线程池最大线程数量
  • 创建surfaceflinger对象
  • 调用surfaceflinger的init函数,然后想servicemanager注册
  • 开始run

surfaceflinger的init函数工作:

  • 初始化opengles
  • 创建显示设备抽象HWComposer,和显示设备打交道
  • 创建显示设备对象DisplayDevice
  • 启动EventThread,监听和处理SurfaceFlinger中的事件
  • 设置Vsync信号周期(在没有HWComposer支持的情况下)
  • 初始化显示设备,调用initializeDisplays完成
  • 启动开机动画

5.2 surfaceflinger中的消息和事件分发

MessageQueue:用于消息和事件的分发
EventThread:用来分析vsync信号

在SurfaceFlinger对象会有一个成员变量mEventQueue,在SurfaceFlinger的onFirstRef的时候会对mEventQueue进行init,创建handler和looper。

handler是MessageQueue的内部类,主要用来处理三个消息。

image.png

最后在SurfaceFlinger调用run函数,这个函数最后会调用mEventQueue.waitMessage().


image.png

看下waitMessage方法,flushCommands主要是清理工作的,和Binder驱动的交互关了。而pollOnce是消息机制,主要调用了epoll_wait函数,会阻塞,阻塞完了会分发消息队列中的消息。这里的消息只有自己在Handler中发的消息,还有在setEventThread中自己添加的fd。


image.png

这里是handler的分发消息。

接下来在surfaceflinger创建的时候会设置mSFEventThread线程用来分发Vsync消息。


image.png

5.3 显示设备类DisplayDevice

DisplayDevice是显示设备的抽象,定义了三种类型的设备:

  • DISPLAY_PRIMARY 主显示设备,通常是LCD屏幕
  • DISPLAY_EXTERNAL 扩展显示设备,通过HDMI输出显示信号
  • DISPLAY_VIRTUAL 虚拟显示设备,通过WIFI输出信号

surfaceFlinger中需要显示的图层(layer)将通过DisplayDevice对象传递到OpenGLES中进行合成,合成之后的图像再通过HWComposer对象传递到Framebuffer中显示。DisplayDevice对象中的成员变量mVisibleLayersSortedByZ保存了所有需要显示在本显示设备中显示的Layer对象,同时DisplayDevice对象也保存了和显示设备相关的显示方向、显示区域坐标等信息。

所有显示设备的输出都要通过HWComposer对象完成,因此上面这段代码先调用了HWComposer的isConnected来检查显示设备是否已连接,只有和显示设备连接的DisplayDevice对象才会被创建出来。即使没有任何物理显示设备被检测到,SurfaceFlinger都需要一个DisplayDevice对象才能正常工作,因此,DISPLAY_PRIMARY类型的DisplayDevice对象总是会被创建出来。

然后会调用createBufferQueue函数创建一个producer和consumer,这个之前分析过。然后又创建了一个FramebufferSurface对象。这里我们看到在新建FramebufferSurface对象时把consumer参数传入了代表是一个消费者。而在DisplayDevice的构造函数中,会创建一个Surface对象传递给底层的OpenGL ES使用,而这个Surface是一个生产者。在OpenGl ES中合成好了图像之后会将图像数据写到Surface对象中,这将触发consumer对象的onFrameAvailable函数被调用:

这就是Surface数据好了就通知消费者来拿数据做显示用,在onFrameAvailable函数汇总,通过nextBuffer获得图像数据,然后调用HWComposer对象mHwc的fbPost函数输出。 fbPost最后调用gralloc模块的post函数进行输出。

DisplayDevice内部, DisplayDevice构造函数主要功能是创建了一个Surface对象mNativeWindow,同时用它作为参数创建EGLSurface对象,这个EGLSurface对象是OpenGL ES中绘图需要的。

这样,在DisplayDevice中就建立了一个通向Framebuffer的通道,只要向DisplayDevice的mSurface写入数据。就会到消费者FrameBufferSurface的onFrameAvailable函数,然后到HWComposer在到Gralloc模块,最后输出到显示设备。

swapBuffers函数将内部缓冲区的图像数据刷新到显示设备的Framebuffer中,它通过调用eglSwapBuffers函数来完成缓冲区刷新工作。但是注意调用swapBuffers输出图像是在显示设备不支持硬件composer的情况下。

5.4 VSYNC信号的分发(重要)

Vsync信号非常重要,决定这cpu什么时候开始准备数据,所以理解vsync信号的转发过程有利于理解屏幕的刷新机制。

android 屏幕刷新机制参考:https://www.cnblogs.com/dasusu/p/8311324.html

image.png

流程一 vsync到来的时候:
HWComposer::vsync------> mEventHandler.onVSyncReceived------->mPrimaryDispSync.addResyncSample(timestamp)------->updateModeLocked() ------->mThread->updateModel(mThread是DispSyncThread类)----> mCond.signal()发送信号唤醒其他阻塞的等待。

流程二 唤醒:
----> DispSyncThread::threadLoop()---->fireCallbackInvocations(callbackInvocations)回调注册的监听器-----> callbacks[i].mCallback->onDispSyncEvent()

流程三 流程二的唤醒回调去了哪里?

  • 回调监听注册: 在EventThread的Threadloop函数中会根据waitForEvent函数判断是否需要注册监听---> 如果没有客户端和EventThread建立连接就不注册监听vsync信号的回调(流程2中的回调)-----> 通过mVSyncSource->setCallback函数注册回调监听。

mVSyncSource来源如下图所示。


image.png
  • 回调 调用到EventThread::onVSyncEvent---> 把mVSyncEvent放到数组第一个,然后调用Condition的broadcast,----> 唤醒EventThread::threadLoop的等待-----> 调用conn->postEvent来发送Event事件。
    ---->将事件发送到了MessageQueue

接下来

MessageQueue分发Vsync

image.png

MessageQueue::cb_eventReceiver() ----> MessageQueue::eventReceiver-----> MessageQueue::Handler::dispatchRefresh()-------> SurfaceFlinger::onMessageReceiver------> handleMessageRefresh。开始负责刷新系统显示。

这样就从底层到了SurfaceFlinger的接收vsync并刷新显示

Vsync小结:我们回顾下整个流程,VSync信号从底层产生后,经过几个函数,保存到了SurfaceFlinger的mPrimaryDispSync变量(DisySync类)的数组中,这样设计的目的让底层的调用尽快结束,否则会耽搁下次VSync信号的发送。然后在mPrimaryDispSync变量关联的线程开始分发数组中的VSync信号,分发的过程也调用了几个回调函数,最终结果是放在EventThread对象的数组中。EventThread是转发VSync信号的中心。不但会把VSync信号发给SurfaceFlinger,还会把信号发送到用户进程中去。EventThread的工作比较重,因此SurfaceFlinger中使用了两个EventThread对象来转发VSync信号。确保能及时转发。SurfaceFlinger中的MessageQueue收到Event后,会将Event转化成消息发送,这样最终就能在主线程调用SurfaceFlinger的函数处理VSync信号了。

接下来vsync信号是怎么样转发到用户进程去?



6 图像的输出过程

在五部分的时候通过vsync信号调用到了surfaceflinger的handleMessageRefresh函数

image.png

其中比较重要的几个函数如下:

preComposition()
rebuildLayerStacks();
setUpHWComposer()
doDebugFlashRegions();
doComposition();
postComposition();

这里的几个函数可以结合systrace的surfaceflinger部分查看流程。

----> 开始

image.png

上面函数先是调用了mDrawingState的layersSortedByZ来得到上次绘图的Layer层列表。并不是所有的Layer都会参与屏幕图像的绘制,因此SurfaceFlinger用state对象来记录参与绘制的Layer对象。
然后遍历所有图层检查是否有变化,有变化就调用signalLayerUpdate()通知这种变化
----> 自然引出怎么判断图层是否有变化? 根据以下三个变量

image.png

通过之前的部分的流程,知道了在Layer层发生变化的时候会调用图层的onFrameAvailable来通知这种变化,
设置监听器: SurfaceFlingerConsumer::setContentChangedListener
BufferQueue::ProxyConsumerListener::onFrameAvailable ------> ConsumerBase::onFrameAvailable ----> Layer::onFrameAvailable(主要目的将mQueueFrames加1,然后调用了SurfaceFlinger的signalLayerUpdate) -----> ........----> SurfaceFlinger::onMessageReceived----->handleMessageTransaction和handleMessageInvalidate

handleMessageInvalidate函数中调用了handlePageFlip函数,这个函数将会处理Layer中的缓冲区,把更新过的图像缓冲区切换到前台,等待VSync信号更新到FrameBuffer。

image.png

用户进程更新Surface图像,将导致SurfaceFlinger中的Layer发送invalidate消息,处理该消息会调用handleTransaction函数和handlePageFilp函数来更新Layer对象。一旦VSync信号到来,再调用rebuildlayerStacks setUpHWComposer doComposition postComposition函数将所有Layer的图像混合后更新到显示设备上去。

rebuildLayerStacks函数的作用是重建每个显示设备的可见layer对象列表.
setUpHWComposer函数的作用是更新HWComposer对象中图层对象列表以及图层属性。
doComposition函数是合成所有层的图像。 doComposition函数针对每种显示设备调用doDisplayComposition函数来合成,合成后调用postFramebuffer函数

doDisplayComposition函数根据显示设备支持的更新方式,重新设置需要更新区域的大小。
真正的合成工作是在doComposerSurfaces函数中完成,这个函数在layer的类型为HWC_FRAMEBUFFER,或者不支持硬件的composer的情况下,调用layer的draw函数来一层一层低合成最后的图像。
合成完后,doDisplayComposition函数调用了hw的swapBuffers函数,这个函数前面介绍过了,它将在系统不支持硬件的composer情况下调用eglSwapBuffers来输出图像到显示设备。

postFramebuffer

postFramebuffer先判断系统是否支持composer,如果不支持,我们知道图像已经在doComposition函数时调用hw->swapBuffers输出了,就返回了。如果支持硬件composer,postFramebuffer函数将调用HWComposer的commit函数继续执行。commit函数又调用了composer模块的set接口来完成工作,这就到HAL层的代码了,最后输出到显示屏上。

这些函数更具体的信息还是需要参考阅读原文或者源码。https://blog.csdn.net/kc58236582/article/details/52778333

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容