【閱讀筆記】Surface系統(tǒng)

參考:
【Android6.0 顯示系統(tǒng)(一) Surface創(chuàng)建】
https://blog.csdn.net/kc58236582/article/details/52670528
【Android6.0 顯示系統(tǒng)(二) SurfaceFlinger創(chuàng)建Surface】
https://blog.csdn.net/kc58236582/article/details/52680288
【Android6.0 顯示系統(tǒng)(三) 管理圖像緩沖區(qū)】
https://blog.csdn.net/kc58236582/article/details/52681363
【Android6.0 顯示系統(tǒng)(四) 圖像顯示相關(guān)】
https://blog.csdn.net/kc58236582/article/details/52690130
【Android6.0 顯示系統(tǒng)(五) SurfaceFlinger服務(wù)】
https://blog.csdn.net/kc58236582/article/details/52763534
【Android6.0 顯示系統(tǒng)(六) 圖像的輸出過程】
https://blog.csdn.net/kc58236582/article/details/52778333

OpenGrok地址http://androidxref.com/

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


1 Surface的創(chuàng)建

1.1 應(yīng)用層創(chuàng)建Surface
1.2 WMS創(chuàng)建Surface

1.1 應(yīng)用層創(chuàng)建Surface

一般Activity中的Surface是ViewRootImpl中通過WMS創(chuàng)建的碾盟。當(dāng)然也會有需要在應(yīng)用中獨立創(chuàng)建Surface的情況,比如相機源譬,視頻播放器等應(yīng)用。它們是通過SurfaceView來使用surface.

  • SurfaceVIew中---> new Surface()
image.png
  • surface的updatewindow函數(shù)
    【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通信冗恨, 在調(diào)用relayout函數(shù)的時候通過outSurface從WMS獲得Surface對象诈泼, 這里的outSurface就是傳入的mNewSurface压恒。返回是通過Parcel的方式

  • Surface的獲取流程
    【Surface】readFrameParcel——> 【Surface】nativeReadFromParcel(在native層創(chuàng)建一個surface對象) ----->【Surface】setNativeObjectLocked(將native層創(chuàng)建的對象保存在java的本地對象中)

在調(diào)用nativeReadFromParcel在native層創(chuàng)建surface對象的時候會從binder中讀取進(jìn)程間傳輸?shù)腷inder窗轩,并將讀取的binder對象作為參數(shù)創(chuàng)建一個IGraphicBufferProducer對象桩撮,然后在以該對象創(chuàng)建Surface

小結(jié)雷绢,在應(yīng)用層使用surface最終也是在native層創(chuàng)建一個surface對象难咕,并且對應(yīng)著一個IGraphicBufferProducer

image.png

1.2 WMS層創(chuàng)建Surface

在應(yīng)用層通過relayout調(diào)用后通過binder的通信最終會調(diào)用到WMS.relayout课梳,這個時候就開始進(jìn)行WMS層的Surface創(chuàng)建過程了。

  • 【W(wǎng)MS】relayoutWindow --->【W(wǎng)MS】 winAnimator.createSurfaceLocked(獲得SurfaceControl對象)---.>outSurface.copyFrom ---> 把native層的surfacecontrol對象中的surface復(fù)制到Surface的mNative中余佃。

  • SurfaceControl的創(chuàng)建
    【W(wǎng)MS】winAnimator.createSurfaceLocked---> 【SurfaceControl】nativeCreate(創(chuàng)建一個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對象。

這里出現(xiàn)了幾個對象:
IWindowSession: 跨進(jìn)程通信的接口爆土,供客戶端使用椭懊,服務(wù)端實現(xiàn)在Session
Session:對IWindowSession的實現(xiàn),并封裝了WMS的實例步势,可以和WMS交互氧猬;同時在addToDisplay的時候把客戶端的IWindow對象傳遞給了WMS,這樣WMS就可以管理客戶端的Window坏瘩。
SurfaceSession:java層的對象盅抚,對應(yīng)了natvie層的SurfaceComposerClient
SurfaceComposerClient: 在底層和Surface可以建立通信。

  • SurfaceSession的創(chuàng)建
    在ViewRootImpl的setView時候桑腮,會調(diào)用mWindowSession(IWindowSession-->實現(xiàn)在Session.java中)的addToDisplay函數(shù)泉哈,在這個函數(shù)調(diào)用了WMS的addWindow函數(shù),而在addWindow方法破讨,后面會創(chuàng)建一個WindowState對象丛晦,然后調(diào)用了其attach方法。

【W(wǎng)indowState】attach--->【Session】windowAddedLocked --->【Session】new SurfaceSession()【創(chuàng)建SurfaceSession提陶,同時讓wms記錄該條surfacesession】---> 【SurfaceSession】nativeCreate
----> 【android_view_SurfaceSession】nativeCreate(在native層創(chuàng)建SurfaceComposerClient對象烫沙,該對象可以連接到SurfaceFlinger)

  • SurfaceComposerClient連接SurfaceFlinger
    負(fù)責(zé)連接到SurfaceFlinger,并獲得SurfaceFinger端為該客戶端創(chuàng)建的client對象隙笆,保存在SurfaceComposerClient中的mClient對象中锌蓄。當(dāng)需要創(chuàng)建Surface的時候會調(diào)用到SurfaceFlinger中去。
    比如在創(chuàng)建Surface的時候撑柔, 是通過創(chuàng)建java層的SurfaceControl ---> 獲得native層的SurfaceComposerClient對象--->通過SurfaceComposerClient對象來中和Surfaceflinger連接的mClient對象調(diào)用到SurfaceFlinger ---> SurfaceFlinger創(chuàng)建gbp(graphicBufferProducer,handle)對象瘸爽,然后封裝到native層的SurfaceControl中,返回給客戶端java層铅忿,因此后續(xù)通過java層的SurfaceControl對象就可以通過JNI層獲得native層的SurfaceControl對象剪决,并獲得SurfaceFlinger端創(chuàng)建的gbp
WMS獲取Surface.png

**涉及到的類:SurfaceView, SurfaceControl, SurfaceComposerClient柑潦,SurfaceControl, **



2 SurfaceFlinger創(chuàng)建Surface

2.1 創(chuàng)建Layer
2.2 獲取gbp

在第一部分中已知從java層調(diào)用到了SurfaceFlinger層創(chuàng)建Surface享言,現(xiàn)在就從SurfaceFlinger的createSurface入手。

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp<IBinder>* handle, //唯一標(biāo)識渗鬼, 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();
}

這個函數(shù)中定義了一個消息類MessageCreateLayer览露,然后把它的對象通過postMessageSync方法發(fā)送出去,這個消息是以同步的方式發(fā)送譬胎,因此函數(shù)結(jié)束后可以直接返回結(jié)果差牛。因此就直接到了handler函數(shù),在這個函數(shù)中調(diào)用了SurfaceFlinger的createLayer函數(shù)银择。

2.1 創(chuàng)建Layer

【SurfaceFlinger】createLayer ---> 創(chuàng)建不同的Layer ---> 然后從layer中獲得handle, gbp.

2.1 獲取handle

下面我們再來看handle的獲取多糠,只是新建一個Handle累舷,而這個Handle只是一個Binder的實現(xiàn)浩考,就是標(biāo)識Surface的全局唯一性,沒有什么實際的內(nèi)容被盈。

/*
 * 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

這里出現(xiàn)了三個新的類:
BufferQueue, 隊列,連接producer和consumer的核心只怎。
MonitoredProducer, 只是一個代理類袜瞬,真正工作的是參數(shù)producer, 表示生產(chǎn)者身堡。
SurfaceFlingerConsumer, 消費者代理方邓尤,surflinger消耗方。

image.png

在BufferQueue創(chuàng)建生產(chǎn)者和消費者的具體實現(xiàn)贴谎,是創(chuàng)建了三個對象:BufferQueueCore, BufferqueueProducer, BufferQueueConsumer 汞扎, 這三個對象非常重要。

image.png


3 管理圖像緩沖區(qū)

3.1 生產(chǎn)者和core的聯(lián)系
3.2 消費者和core聯(lián)系
3.3 三者聯(lián)系
3.4 GraphicBuffer對象的創(chuàng)建
3.5 內(nèi)存緩沖區(qū)的fd傳遞到客戶進(jìn)程

  • 最后分析到MonitoredProducer對象擅这,這個對象只是一個代理澈魄,真正實是BufferQueueProducer類,這個對象和BufferQueueCore有關(guān)聯(lián)仲翎,可以管理最多達(dá)64塊的緩沖區(qū)痹扇。Surface可以理解為一張畫布,那么Surface為何要和一個緩沖區(qū)隊列相關(guān)呢溯香?在播放動畫時鲫构,美妙至少要播放24幀畫面才能形成比較真實的動畫效果。而這些數(shù)據(jù)是通過cpu解碼得到的玫坛,準(zhǔn)備他們需要時間结笨。對于圖像顯示設(shè)備而言,刷新周期是固定的,我們必須要在它需要數(shù)據(jù)的時候把數(shù)據(jù)準(zhǔn)備好禀梳。視頻播放的每一幀也需要在指定的時間播放杜窄,因此解碼器會提前準(zhǔn)備好一批數(shù)據(jù),這些數(shù)據(jù)保存在解碼器內(nèi)存的緩沖區(qū)中算途,當(dāng)時間到達(dá)是塞耕,解碼器會把內(nèi)部緩沖區(qū)的圖像復(fù)制到Surface中,但是顯示設(shè)備并不是立刻就把數(shù)據(jù)取走的嘴瓤,因此Surface也需要緩沖區(qū)來臨時保存數(shù)據(jù)扫外。

3.1 生產(chǎn)者和core的聯(lián)系

BufferQueueProducer是IGraphicBufferProducer的實現(xiàn)類,IGraphicBufferProducer中定義了使用buffer的一些方法接口廓脆。
一般在使用這個buffer之前會先connect筛谚, 在使用結(jié)束后會disconnect

在BufferQueueCore類中定義了一個64項的數(shù)據(jù)mSlots。

 BufferQueueDefs::SlotsType mSlots;

每個緩沖區(qū)的類型是BufferSlot類型停忿。它有兩個重要的成員變量驾讲,mGraphicBuffer是指向圖像緩沖區(qū)GraphicBuffer的指針,mBufferState表示圖像緩沖區(qū)的狀態(tài)席赂。

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

BufferState的狀態(tài)有下面幾個

enum BufferState {
        FREE = 0,//空閑
        DEQUEUED = 1,//生產(chǎn)狀態(tài)吮铭,被生產(chǎn)者擁有
        QUEUED = 2,//保存數(shù)據(jù)狀態(tài),被BufferQueue擁有
        ACQUIRED = 3//消費狀態(tài)颅停,被消費者擁有
    };

BufferQueueProducer的dequeueBuffer函數(shù)用來向BufferQueueCore申請一個空閑的slot谓晌,這個slot可能已經(jīng)有緩沖區(qū),也可能沒有癞揉,如果沒有緩沖區(qū)纸肉,dequeueBuffer函數(shù)會分配一塊新的緩沖區(qū)。得到空閑的slot后喊熟,還?需要調(diào)用requestBuffer函數(shù)來取得一塊緩沖區(qū)??柏肪。得到緩沖區(qū),如果不需要了逊移,可以使用cancelBuffer函數(shù)來釋放這個slot预吆。調(diào)用dequeueBuffer函數(shù)之后,緩沖區(qū)的擁有者是生產(chǎn)者胳泉,緩沖區(qū)處于DEQUEUED狀態(tài)拐叉。一旦緩沖區(qū)復(fù)制數(shù)據(jù)完成,通過queueBuffer函數(shù)把緩沖區(qū)的控制權(quán)交還給BufferQueueCore扇商,這時候緩沖區(qū)將處于QUEUED狀態(tài)

3.2 消費者和core聯(lián)系

IGraphicBufferConsumer定義接口凤瘦,實際實現(xiàn)類是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的實現(xiàn),在使用它之前案铺,先要調(diào)用connect函數(shù)建立聯(lián)系蔬芥,這里傳遞的參數(shù)是IConsumerListener對象,是一個回調(diào)接口,如果BufferQueue中有數(shù)據(jù)準(zhǔn)備好了就會調(diào)用它的onFrameAvailable函數(shù)來通知消費者取走數(shù)據(jù)笔诵。
取走數(shù)據(jù)的時候返吻,需要調(diào)用acquireBuffer函數(shù),將緩沖區(qū)狀態(tài)變成ACQUIRED乎婿,使用完之后調(diào)用releaseBuffer函數(shù)可以吧緩沖區(qū)數(shù)據(jù)歸還給BufferQueueCore测僵,這樣緩沖區(qū)就變成FREE

3.3 三者聯(lián)系

對象BufferQueueProducer和BufferQueueConsumer好像沒有直接聯(lián)系谢翎,其實都是通過共同的BufferQueueCore對象連接在一起的捍靠,很多操作時直接使用BufferQueueCore對象的成員變量而不是函數(shù)來完成的。

3.4 GraphicBuffer對象的創(chuàng)建

dequeueBuffer函數(shù)的部分代碼森逮,在從BufferQueueCore中獲取到slot的時候榨婆,如果需要重新分配圖像緩沖區(qū)就會調(diào)用mCore->mAllocator->createGraphicBuffer函數(shù)來重新創(chuàng)建一個圖像緩沖區(qū)。

        ......
        *outSlot = found;//found復(fù)制到outslot
        ATRACE_BUFFER_INDEX(found);
 
        attachedByConsumer = mSlots[found].mAttachedByConsumer;
 
        mSlots[found].mBufferState = BufferSlot::DEQUEUED;//slot的狀態(tài)修改變成生產(chǎn)狀態(tài)
 
        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;//需要重啟分配緩沖區(qū)
        } 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) {//如果需要重啟分配圖像緩沖區(qū)
        status_t error;
        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
        sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(//創(chuàng)建圖像緩沖區(qū)
                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是調(diào)用到了SurfaceFlinger中創(chuàng)建的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;
}

創(chuàng)建一個GraphicBuffer璃搜, 在這里創(chuàng)建了一個graphicBuffer拖吼,在GraphicBuffer的構(gòu)造函數(shù)中會通過GraphicBufferAllocator進(jìn)行實際內(nèi)存的分配。

GraphicBufferAllocator實際是裝載了Gralloc模塊这吻,實際通過Gralloc模塊進(jìn)行分配,這個模塊是在GraphicBufferAllocator的構(gòu)造函數(shù)中裝載篙议。
調(diào)用alloc分配了一塊共享的內(nèi)存緩沖區(qū)唾糯,alloc函數(shù)將返回共享區(qū)的fd和緩沖區(qū)的指針。既然GraphicBuffer中的緩沖區(qū)是共享內(nèi)存鬼贱,我們知道使用共享內(nèi)存需要傳遞共享內(nèi)存的句柄fd移怯。下面我們看看是如何傳到客戶進(jìn)程的。

3.5 內(nèi)存緩沖區(qū)的fd傳遞到客戶進(jìn)程(暫時略過)

通過序列化的方式傳遞
GraphicBuffer類從模板類Flattenable派生这难,這個派生類可以通過Parcel傳遞舟误,通常派生類需要重載flatten和unflatten方法,用于對象的序列化和反序列化姻乓。

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

通過flatten函數(shù)嵌溢,中fds用來傳遞文件句柄,函數(shù)把handle中的句柄復(fù)制到fds中蹋岩,然后這些句柄就可以在通過binder傳遞到目標(biāo)進(jìn)程中去赖草。

然后在unflatten函數(shù)調(diào)用時候,共享文件句柄已經(jīng)準(zhǔn)備好了剪个,但是內(nèi)存還沒有進(jìn)行映射秧骑;調(diào)用mBufferMapper.registerBuffer函數(shù)進(jìn)行內(nèi)存映射,實際調(diào)用到了Gralloc模塊的gralloc_register_buffer函數(shù),這個函數(shù)就調(diào)用了mmap進(jìn)行共享內(nèi)存映射乎折。

在硬件設(shè)備支持Framebuffer緩沖區(qū)的情況下绒疗,Surface中繪制圖形的緩沖區(qū)就是Framebuffer的緩沖區(qū),繪制完成后骂澄,如果不需要進(jìn)行圖像合成忌堂,只需要flip操作就能完成圖像的輸出,中間完全不用復(fù)制的過程酗洒,很高效士修。

image.png

小結(jié): 這部分作者kc專欄主要講解了在surface端申請的緩沖是如何管理的。以及graphicbuffer的分配和傳遞方式樱衷。



4 圖像顯示相關(guān)內(nèi)容

概念:刷新率棋嘲,幀率,幀緩沖(framebuffer)矩桂,雙緩沖技術(shù)沸移,Vsync信號,三重緩沖技術(shù)侄榴。
VSync是一個硬件信號雹锣,一般是顯示器刷新周期到了會發(fā)送。
HWComposer: 表示顯示的硬件設(shè)備癞蚕。

HwComposer----> loadFbHalModule(裝載Framebuffer的硬件模塊) -----> loadHwcModule(裝載HWComposer的硬件模塊) ---->根據(jù)方式不同產(chǎn)生不同的vsync來源(軟件模擬或硬件產(chǎn)生)


image.png

4.1 Framebuffer原理

這里的講解流程比較簡單蕊爵,主要是通過gralloc模塊打開kernel提供過的fb節(jié)點。直接根據(jù)kc專欄的分析畫個流程圖便于自己今后快速查看桦山。

image.png

分配緩沖圖像緩沖區(qū)內(nèi)存: 最終實現(xiàn)是通過galloc模塊的gralloc_alloc
小結(jié):這節(jié)主要清楚圖像是怎么顯示攒射,什么是framebuffer,雙緩沖恒水,以及vsync即可会放。



5 SurfaceFlinger

surfaceflinger服務(wù)是一個native服務(wù),是圖像系統(tǒng)非常重要的一個服務(wù)钉凌,負(fù)責(zé)圖層的合成咧最,vsync的分發(fā),以及和hal層的交互到圖像的渲染御雕。
surfaceflinger怎么啟動的就不用多看(通過.rc配置在Init階段啟動矢沿,很多native層服務(wù)都是,native層的守護進(jìn)程同樣如此比如vold饮笛,healthd咨察,netd等)。啟動過后surfaceflinger干了什么事情是需要重點關(guān)注福青。

5.1 服務(wù)啟動

在啟動的時候調(diào)用到了main_surfaceflinger.cpp中的main函數(shù)摄狱,在這里干了幾件事:

  • 配置binder線程池最大線程數(shù)量
  • 創(chuàng)建surfaceflinger對象
  • 調(diào)用surfaceflinger的init函數(shù)脓诡,然后想servicemanager注冊
  • 開始run

surfaceflinger的init函數(shù)工作:

  • 初始化opengles
  • 創(chuàng)建顯示設(shè)備抽象HWComposer,和顯示設(shè)備打交道
  • 創(chuàng)建顯示設(shè)備對象DisplayDevice
  • 啟動EventThread媒役,監(jiān)聽和處理SurfaceFlinger中的事件
  • 設(shè)置Vsync信號周期(在沒有HWComposer支持的情況下)
  • 初始化顯示設(shè)備祝谚,調(diào)用initializeDisplays完成
  • 啟動開機動畫

5.2 surfaceflinger中的消息和事件分發(fā)

MessageQueue:用于消息和事件的分發(fā)
EventThread:用來分析vsync信號

在SurfaceFlinger對象會有一個成員變量mEventQueue,在SurfaceFlinger的onFirstRef的時候會對mEventQueue進(jìn)行init酣衷,創(chuàng)建handler和looper交惯。

handler是MessageQueue的內(nèi)部類,主要用來處理三個消息穿仪。

image.png

最后在SurfaceFlinger調(diào)用run函數(shù)席爽,這個函數(shù)最后會調(diào)用mEventQueue.waitMessage().


image.png

看下waitMessage方法,flushCommands主要是清理工作的啊片,和Binder驅(qū)動的交互關(guān)了只锻。而pollOnce是消息機制,主要調(diào)用了epoll_wait函數(shù)紫谷,會阻塞齐饮,阻塞完了會分發(fā)消息隊列中的消息。這里的消息只有自己在Handler中發(fā)的消息笤昨,還有在setEventThread中自己添加的fd祖驱。


image.png

這里是handler的分發(fā)消息。

接下來在surfaceflinger創(chuàng)建的時候會設(shè)置mSFEventThread線程用來分發(fā)Vsync消息瞒窒。


image.png

5.3 顯示設(shè)備類DisplayDevice

DisplayDevice是顯示設(shè)備的抽象捺僻,定義了三種類型的設(shè)備:

  • DISPLAY_PRIMARY 主顯示設(shè)備,通常是LCD屏幕
  • DISPLAY_EXTERNAL 擴展顯示設(shè)備根竿,通過HDMI輸出顯示信號
  • DISPLAY_VIRTUAL 虛擬顯示設(shè)備陵像,通過WIFI輸出信號

surfaceFlinger中需要顯示的圖層(layer)將通過DisplayDevice對象傳遞到OpenGLES中進(jìn)行合成,合成之后的圖像再通過HWComposer對象傳遞到Framebuffer中顯示寇壳。DisplayDevice對象中的成員變量mVisibleLayersSortedByZ保存了所有需要顯示在本顯示設(shè)備中顯示的Layer對象,同時DisplayDevice對象也保存了和顯示設(shè)備相關(guān)的顯示方向妻怎、顯示區(qū)域坐標(biāo)等信息壳炎。

所有顯示設(shè)備的輸出都要通過HWComposer對象完成,因此上面這段代碼先調(diào)用了HWComposer的isConnected來檢查顯示設(shè)備是否已連接逼侦,只有和顯示設(shè)備連接的DisplayDevice對象才會被創(chuàng)建出來匿辩。即使沒有任何物理顯示設(shè)備被檢測到,SurfaceFlinger都需要一個DisplayDevice對象才能正常工作榛丢,因此铲球,DISPLAY_PRIMARY類型的DisplayDevice對象總是會被創(chuàng)建出來。

然后會調(diào)用createBufferQueue函數(shù)創(chuàng)建一個producer和consumer晰赞,這個之前分析過稼病。然后又創(chuàng)建了一個FramebufferSurface對象选侨。這里我們看到在新建FramebufferSurface對象時把consumer參數(shù)傳入了代表是一個消費者。而在DisplayDevice的構(gòu)造函數(shù)中然走,會創(chuàng)建一個Surface對象傳遞給底層的OpenGL ES使用援制,而這個Surface是一個生產(chǎn)者。在OpenGl ES中合成好了圖像之后會將圖像數(shù)據(jù)寫到Surface對象中芍瑞,這將觸發(fā)consumer對象的onFrameAvailable函數(shù)被調(diào)用:

這就是Surface數(shù)據(jù)好了就通知消費者來拿數(shù)據(jù)做顯示用晨仑,在onFrameAvailable函數(shù)匯總,通過nextBuffer獲得圖像數(shù)據(jù)拆檬,然后調(diào)用HWComposer對象mHwc的fbPost函數(shù)輸出洪己。 fbPost最后調(diào)用gralloc模塊的post函數(shù)進(jìn)行輸出。

DisplayDevice內(nèi)部竟贯, DisplayDevice構(gòu)造函數(shù)主要功能是創(chuàng)建了一個Surface對象mNativeWindow答捕,同時用它作為參數(shù)創(chuàng)建EGLSurface對象,這個EGLSurface對象是OpenGL ES中繪圖需要的澄耍。

這樣噪珊,在DisplayDevice中就建立了一個通向Framebuffer的通道,只要向DisplayDevice的mSurface寫入數(shù)據(jù)齐莲。就會到消費者FrameBufferSurface的onFrameAvailable函數(shù)痢站,然后到HWComposer在到Gralloc模塊,最后輸出到顯示設(shè)備选酗。

swapBuffers函數(shù)將內(nèi)部緩沖區(qū)的圖像數(shù)據(jù)刷新到顯示設(shè)備的Framebuffer中阵难,它通過調(diào)用eglSwapBuffers函數(shù)來完成緩沖區(qū)刷新工作。但是注意調(diào)用swapBuffers輸出圖像是在顯示設(shè)備不支持硬件composer的情況下芒填。

5.4 VSYNC信號的分發(fā)(重要)

Vsync信號非常重要呜叫,決定這cpu什么時候開始準(zhǔn)備數(shù)據(jù),所以理解vsync信號的轉(zhuǎn)發(fā)過程有利于理解屏幕的刷新機制殿衰。

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()發(fā)送信號喚醒其他阻塞的等待朱庆。

流程二 喚醒:
----> DispSyncThread::threadLoop()---->fireCallbackInvocations(callbackInvocations)回調(diào)注冊的監(jiān)聽器-----> callbacks[i].mCallback->onDispSyncEvent()

流程三 流程二的喚醒回調(diào)去了哪里?

  • 回調(diào)監(jiān)聽注冊: 在EventThread的Threadloop函數(shù)中會根據(jù)waitForEvent函數(shù)判斷是否需要注冊監(jiān)聽---> 如果沒有客戶端和EventThread建立連接就不注冊監(jiān)聽vsync信號的回調(diào)(流程2中的回調(diào))-----> 通過mVSyncSource->setCallback函數(shù)注冊回調(diào)監(jiān)聽闷祥。

mVSyncSource來源如下圖所示娱颊。


image.png
  • 回調(diào) 調(diào)用到EventThread::onVSyncEvent---> 把mVSyncEvent放到數(shù)組第一個,然后調(diào)用Condition的broadcast凯砍,----> 喚醒EventThread::threadLoop的等待-----> 調(diào)用conn->postEvent來發(fā)送Event事件箱硕。
    ---->將事件發(fā)送到了MessageQueue

接下來

MessageQueue分發(fā)Vsync

image.png

MessageQueue::cb_eventReceiver() ----> MessageQueue::eventReceiver-----> MessageQueue::Handler::dispatchRefresh()-------> SurfaceFlinger::onMessageReceiver------> handleMessageRefresh。開始負(fù)責(zé)刷新系統(tǒng)顯示悟衩。

這樣就從底層到了SurfaceFlinger的接收vsync并刷新顯示

Vsync小結(jié):我們回顧下整個流程剧罩,VSync信號從底層產(chǎn)生后,經(jīng)過幾個函數(shù)座泳,保存到了SurfaceFlinger的mPrimaryDispSync變量(DisySync類)的數(shù)組中惠昔,這樣設(shè)計的目的讓底層的調(diào)用盡快結(jié)束幕与,否則會耽擱下次VSync信號的發(fā)送。然后在mPrimaryDispSync變量關(guān)聯(lián)的線程開始分發(fā)數(shù)組中的VSync信號舰罚,分發(fā)的過程也調(diào)用了幾個回調(diào)函數(shù)纽门,最終結(jié)果是放在EventThread對象的數(shù)組中。EventThread是轉(zhuǎn)發(fā)VSync信號的中心营罢。不但會把VSync信號發(fā)給SurfaceFlinger赏陵,還會把信號發(fā)送到用戶進(jìn)程中去。EventThread的工作比較重饲漾,因此SurfaceFlinger中使用了兩個EventThread對象來轉(zhuǎn)發(fā)VSync信號蝙搔。確保能及時轉(zhuǎn)發(fā)。SurfaceFlinger中的MessageQueue收到Event后考传,會將Event轉(zhuǎn)化成消息發(fā)送吃型,這樣最終就能在主線程調(diào)用SurfaceFlinger的函數(shù)處理VSync信號了。

接下來vsync信號是怎么樣轉(zhuǎn)發(fā)到用戶進(jìn)程去僚楞?



6 圖像的輸出過程

在五部分的時候通過vsync信號調(diào)用到了surfaceflinger的handleMessageRefresh函數(shù)

image.png

其中比較重要的幾個函數(shù)如下:

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

這里的幾個函數(shù)可以結(jié)合systrace的surfaceflinger部分查看流程勤晚。

----> 開始

image.png

上面函數(shù)先是調(diào)用了mDrawingState的layersSortedByZ來得到上次繪圖的Layer層列表。并不是所有的Layer都會參與屏幕圖像的繪制泉褐,因此SurfaceFlinger用state對象來記錄參與繪制的Layer對象赐写。
然后遍歷所有圖層檢查是否有變化,有變化就調(diào)用signalLayerUpdate()通知這種變化
----> 自然引出怎么判斷圖層是否有變化膜赃? 根據(jù)以下三個變量

image.png

通過之前的部分的流程挺邀,知道了在Layer層發(fā)生變化的時候會調(diào)用圖層的onFrameAvailable來通知這種變化,
設(shè)置監(jiān)聽器: SurfaceFlingerConsumer::setContentChangedListener
BufferQueue::ProxyConsumerListener::onFrameAvailable ------> ConsumerBase::onFrameAvailable ----> Layer::onFrameAvailable(主要目的將mQueueFrames加1,然后調(diào)用了SurfaceFlinger的signalLayerUpdate) -----> ........----> SurfaceFlinger::onMessageReceived----->handleMessageTransaction和handleMessageInvalidate

handleMessageInvalidate函數(shù)中調(diào)用了handlePageFlip函數(shù)跳座,這個函數(shù)將會處理Layer中的緩沖區(qū)端铛,把更新過的圖像緩沖區(qū)切換到前臺,等待VSync信號更新到FrameBuffer疲眷。

image.png

用戶進(jìn)程更新Surface圖像禾蚕,將導(dǎo)致SurfaceFlinger中的Layer發(fā)送invalidate消息,處理該消息會調(diào)用handleTransaction函數(shù)和handlePageFilp函數(shù)來更新Layer對象狂丝。一旦VSync信號到來夕膀,再調(diào)用rebuildlayerStacks setUpHWComposer doComposition postComposition函數(shù)將所有Layer的圖像混合后更新到顯示設(shè)備上去。

rebuildLayerStacks函數(shù)的作用是重建每個顯示設(shè)備的可見layer對象列表.
setUpHWComposer函數(shù)的作用是更新HWComposer對象中圖層對象列表以及圖層屬性美侦。
doComposition函數(shù)是合成所有層的圖像。 doComposition函數(shù)針對每種顯示設(shè)備調(diào)用doDisplayComposition函數(shù)來合成魂奥,合成后調(diào)用postFramebuffer函數(shù)

doDisplayComposition函數(shù)根據(jù)顯示設(shè)備支持的更新方式菠剩,重新設(shè)置需要更新區(qū)域的大小。
真正的合成工作是在doComposerSurfaces函數(shù)中完成耻煤,這個函數(shù)在layer的類型為HWC_FRAMEBUFFER,或者不支持硬件的composer的情況下具壮,調(diào)用layer的draw函數(shù)來一層一層低合成最后的圖像准颓。
合成完后,doDisplayComposition函數(shù)調(diào)用了hw的swapBuffers函數(shù)棺妓,這個函數(shù)前面介紹過了攘已,它將在系統(tǒng)不支持硬件的composer情況下調(diào)用eglSwapBuffers來輸出圖像到顯示設(shè)備。

postFramebuffer

postFramebuffer先判斷系統(tǒng)是否支持composer怜跑,如果不支持样勃,我們知道圖像已經(jīng)在doComposition函數(shù)時調(diào)用hw->swapBuffers輸出了,就返回了性芬。如果支持硬件composer峡眶,postFramebuffer函數(shù)將調(diào)用HWComposer的commit函數(shù)繼續(xù)執(zhí)行。commit函數(shù)又調(diào)用了composer模塊的set接口來完成工作植锉,這就到HAL層的代碼了辫樱,最后輸出到顯示屏上。

這些函數(shù)更具體的信息還是需要參考閱讀原文或者源碼俊庇。https://blog.csdn.net/kc58236582/article/details/52778333

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狮暑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辉饱,更是在濱河造成了極大的恐慌搬男,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鞋囊,死亡現(xiàn)場離奇詭異止后,居然都是意外死亡,警方通過查閱死者的電腦和手機溜腐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門译株,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挺益,你說我怎么就攤上這事歉糜。” “怎么了望众?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵匪补,是天一觀的道長。 經(jīng)常有香客問我烂翰,道長夯缺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任甘耿,我火速辦了婚禮踊兜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘佳恬。我一直安慰自己捏境,他們只是感情好于游,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著垫言,像睡著了一般贰剥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筷频,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天蚌成,我揣著相機與錄音,去河邊找鬼截驮。 笑死笑陈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的葵袭。 我是一名探鬼主播涵妥,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼坡锡!你這毒婦竟也來了蓬网?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤鹉勒,失蹤者是張志新(化名)和其女友劉穎帆锋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禽额,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡锯厢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脯倒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片实辑。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖藻丢,靈堂內(nèi)的尸體忽然破棺而出剪撬,到底是詐尸還是另有隱情,我是刑警寧澤悠反,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布残黑,位于F島的核電站,受9級特大地震影響斋否,放射性物質(zhì)發(fā)生泄漏梨水。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一茵臭、第九天 我趴在偏房一處隱蔽的房頂上張望冰木。 院中可真熱鬧,春花似錦、人聲如沸踊沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逼龟。三九已至,卻和暖如春追葡,著一層夾襖步出監(jiān)牢的瞬間腺律,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工宜肉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留匀钧,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓谬返,卻偏偏與公主長得像之斯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子遣铝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容