SurfaceFlinger中Buffer的創(chuàng)建與顯示

Android Surface的創(chuàng)建 已經(jīng)大致說了下Surface在三個進程中創(chuàng)建的過程鸣皂,但是并沒有詳細的說Surface, 那么這個Surface到底是什么呢渣淳? (這里的所指的Surface是Native層的Surface)

先推薦兩篇
Android圖形顯示之硬件抽象層Gralloc族壳,對 Gralloc講得非常非常好
Android顯示系統(tǒng)設計框架介紹 這個寫得很全,也很多茁彭,但是也寫得非常好

前面APP進程已經(jīng)將要繪制的內(nèi)容繪制到了圖形緩沖區(qū)中了,下一步就是SurfaceFlinger進行合成并顯示出來了灭必。

一、Surface到底是什么

1.1 Surface定義

先看下Surface的定義, 如下

class Surface : public ANativeObjectBase<ANativeWindow, Surface, RefBase> {
}

template <typename NATIVE_TYPE, typename TYPE, typename REF>
class ANativeObjectBase : public NATIVE_TYPE, public REF {
}

ANativeObjectBase是一個模板類乃摹,歸根到底就是Surface繼承于ANativeWindow. 那么ANativeWindow又是什么呢禁漓? 看下ANativeWindow的定義, system/core/include/system/window.h

struct ANativeWindow
{
    struct android_native_base_t common;
    const uint32_t flags;
    const int   minSwapInterval;
    const float xdpi, ydpi;
    
    int (*queueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer* buffer, int fenceFd);
    int (*dequeueBuffer)(struct ANativeWindow* window, struct ANativeWindowBuffer** buffer, int* fenceFd);
    int (*perform)(struct ANativeWindow* window, int operation, ... );
}

每種操作系統(tǒng)都定義了自己的窗口系統(tǒng)湃缎,而EGL是定義跨平臺的接口刹缝,Android中EGL中的窗口類型EGLNativeWindowType定義在frameworks/native/opengl/include/EGL/eglplatform.h

#elif defined(__ANDROID__) || defined(ANDROID)  #Android平臺相關(guān)
struct ANativeWindow;
typedef struct ANativeWindow*           EGLNativeWindowType;

EGLNativeWindowType是平臺無關(guān)的牍白,它在Android平臺下被定義成ANativeWindow, 也就是說其實Surface本質(zhì)上就是一個Android的Native Window.

另外 Surface 中還定義了64個BufferSlot

BufferSlot mSlots[NUM_BUFFER_SLOTS];

每個BufferSlot中有一個GraphicBuffer, 這個GraphicBuffer指向了一塊圖形緩沖區(qū)步咪,其實就是一個ASHmem, 與SurfaceFlinger共同映射到同塊內(nèi)存上。 App所有的繪制都是寫入到該圖形緩沖區(qū)的芋类。

所以Surface還包含本地的圖形緩沖區(qū)用于保存UI繪制的內(nèi)容。這個后面會講

1.2 Surface(APP進程)與SurfaceFlinger進程之間的通信

Surface與SurfaceFlinger的通信

RenderThread線程并沒有直接操作Surface, 而是通過操作EGLSurface來間接操作Surface.

以 EglManager中的createSurface為例來簡要的說下,createSurface調(diào)用了eglCreateWindowSurface來創(chuàng)建EGL Surface. 代碼位于 frameworks/native/opengl/libs/EGL/eglApi.cpp

EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
                                    NativeWindowType window,
                                    const EGLint *attrib_list)
{
    egl_connection_t* cnx = NULL;
    egl_display_ptr dp = validate_display_connection(dpy, cnx);
    if (dp) {
        EGLDisplay iDpy = dp->disp.dpy;
        NativeWindowType android_window = NULL;
        if (dp->disp.native == EGL_DEFAULT_DISPLAY) {
            android_window = window; //獲得Native Window, 也就是ANativeWindow
        }

        if (android_window) {
            //調(diào)用native window connect API
            int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);

            if (format != 0) {
                //同樣調(diào)用native window API
                int err = native_window_set_buffers_format(window, format);
            }

            if (dataSpace != 0) {
                int err = native_window_set_buffers_data_space(window, dataSpace);
            }

            ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
            anw->setSwapInterval(anw, 1);
        }

        //OEM 自己的EGL實現(xiàn)磷支, 創(chuàng)建EGLSurface
        EGLSurface surface = cnx->egl.eglCreateWindowSurface(iDpy, config, window, attrib_list);
        if (surface != EGL_NO_SURFACE) {
            //將ANativeWindow封裝到egl_surface_t中,也就是 EGLSurface
            egl_surface_t* s = new egl_surface_t(dp.get(), config, android_window,
                    surface, cnx);
            return s;
        }
    }
}

以native_window_api_connect為例, 代碼位于system/core/include/system/window.h

static inline int native_window_api_connect(
        struct ANativeWindow* window, int api)
{
    return window->perform(window, NATIVE_WINDOW_API_CONNECT, api);
}

由1.1小節(jié)可知 window->perform 也就是調(diào)用ANativeWindow::hook_perform食寡,也就是Surface::hook_perform

int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
    va_list args;
    va_start(args, operation);
    Surface* c = getSelf(window);
    int result = c->perform(operation, args);
    va_end(args);
    return result;
}

而最終就調(diào)用到 Surface::perform中了雾狈,接著調(diào)用到 Surface::dispatchConnect -> Surface::connect

int Surface::connect(int api, const sp<IProducerListener>& listener) {
    IGraphicBufferProducer::QueueBufferOutput output;
    int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
    ...
}

最后通過 (Binder) BpGraphicBufferProducer 去觸發(fā)SurfaceFlinger執(zhí)行connect函數(shù)。

上面就是一個具體的API調(diào)用流程抵皱,其它的API可以類推善榛,只不過對于OEM相關(guān)的EGL實現(xiàn)由于沒有源碼辩蛋,就不好分析了,不過如果要與SurfaceFlinger通信移盆,最終都是一樣的, 都是通過Surface實現(xiàn)的悼院。

好了,Surface是什么大概知道了咒循,并且APP進程是怎么與SurfaceFlinger進程通信的也知道了据途,下面就來看BufferQueue相關(guān)概念

二、BufferQueue

2.1 BufferQueue生產(chǎn)者消費者模型

Android Surface創(chuàng)建 中第4小節(jié)已經(jīng)提到過SurfaceFlinger中Surface(Layer)創(chuàng)建了叙甸,接著又為Layer創(chuàng)建了相關(guān)的BufferQueue颖医。BufferQueue像是一個工具類,通createBufferQueue來建立起B(yǎng)ufferQueue的模型

void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        const sp<IGraphicBufferAlloc>& allocator) {
    sp<BufferQueueCore> core(new BufferQueueCore(allocator));
    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
    *outProducer = producer;
    *outConsumer = consumer;
}
BufferQueue模型

如圖所示, BufferQueue模型是一個典型的生產(chǎn)者/消費者模型裆蒸,主要包含三個重要的類

  • BufferQueueCore

  • BufferQueueProducer
    生產(chǎn)者

  • BufferQueueConsumer
    消費者

2.2 BufferQueueCore重要的變量

BufferQueueCore里有幾個很重要的 set/list定義變量熔萧,用于指明BufferSlot的狀態(tài)

  • mSlots
    定義了64個BufferSlot, BufferSlot直接持有GraphicBuffer

  • std::set<int> mFreeSlots
    mFreeSlots里面的slot值表明當前slot的BufferSlot是FREE狀態(tài), 并且沒有GraphicBuffer

  • std::list<int> mUnusedSlots
    mSlots的大小是64個,而mUnusedSlots就是除掉mFreeSlots剩下的BufferSlot

  • std::list<int> mFreeBuffers
    mFreeBuffers里面的slot表明當前slot對應的BufferSlot是FREE狀態(tài)僚祷,并且有GraphicBuffer

  • std::set<int> mActiveBuffers
    mActiveBuffers里面的slot表明當前slot對應的BufferSlot都有GraphicBuffer佛致,并且是NON FREE狀態(tài)

BufferQueueCore在初始化時就對mFreeSlots/mUnusedSlots初始化了

BufferQueueCore::BufferQueueCore(...)
{
    int numStartingBuffers = getMaxBufferCountLocked();
    for (int s = 0; s < numStartingBuffers; s++) {
        mFreeSlots.insert(s);
    }   
    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
            s++) {
        mUnusedSlots.push_front(s);
    }   
}

getMaxBufferCountLocked是當前Buffer個數(shù), double-buffers 或 triple buffers 又或是single buffers, 現(xiàn)在一般都是triple buffers.

mFreeSlots

2.3 GraphicBuffer

看下GraphicBuffer定義

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

和1.1 Surface定義類似久妆,ANativeObjectBase是一個模板類晌杰,這樣GraphicBuffer就間接繼承 ANativeWindowBuffer與Flattenable類,

GraphicBuffer

fd: 指向一個文件描述符筷弦,這個要么是幀緩沖區(qū)設備肋演,要么是指向一塊圖形緩沖區(qū)(ashmem匿名共享內(nèi)存)
flags: 緩沖區(qū)flag
offset: 緩沖區(qū)偏移
base: 緩沖區(qū)實際地址

參考 Android圖形顯示之硬件抽象層Gralloc

  • 分配圖形緩沖區(qū)
GraphicBuffer::GraphicBuffer 
  -> initSize 
    -> GraphicBufferAllocator::alloc 
      -> alloc_device_t::alloc 
        -> gralloc_alloc
          -> gralloc_alloc_buffer
            -> fd = ashmem_create_region("gralloc-buffer", size);
            -> mapBuffer -> gralloc_map -> mmap

創(chuàng)建一塊匿名共享內(nèi)存,fd 指向這塊內(nèi)存的文件句柄

  • 分配系統(tǒng)幀緩沖區(qū)
    先打開 /dev/graphics/fbXX, 然后做mmap, 并且將映射后的地址保存到 private_module_t -> private_handle_t ->base中
fb_device_open() -> mapFrameBuffer -> mapFrameBufferLocked -> mmap

調(diào)用 gralloc_alloc去分配幀緩存, 也就是使用fb_device_open映射好的那個內(nèi)存空間, 并保存到 GraphicBuffer中的private_handle_t中的base中.
此時 GraphicBuffer中private_handle_t中的fd指向 /dev/graphics/fbXX 的文件句柄

GraphicBuffer::GraphicBuffer 
  -> initSize 
    -> GraphicBufferAllocator::alloc 
      -> alloc_device_t::alloc 
        -> gralloc_alloc
          -> gralloc_alloc_framebuffer
            -> gralloc_alloc_framebuffer_locked

也就是說應用進程分配一塊圖形緩沖區(qū)烂琴,并且將這塊圖形緩沖區(qū)映射到應用進程的地址空間中爹殊,這樣應用程序就可以往里面寫入繪制畫面的內(nèi)容了。 最后應用程序?qū)蕚浜玫膱D形緩沖區(qū)渲染到幀緩沖區(qū)中(通過fb設備)奸绷, 即將圖形緩沖區(qū)的內(nèi)容繪制到顯示屏中去梗夸。參考Android幀緩沖區(qū)(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現(xiàn)原理分析
另外Android圖形顯示之硬件抽象層Gralloc對Gralloc寫得非常非常好。

下面以gralloc_alloc_buffer為例

static int gralloc_alloc_buffer(alloc_device_t* dev,
        size_t size, int /*usage*/, buffer_handle_t* pHandle)
{
    int err = 0;
    int fd = -1; 

    size = roundUpToPageSize(size);
        
    fd = ashmem_create_region("gralloc-buffer", size);

    if (err == 0) {
        private_handle_t* hnd = new private_handle_t(fd, size,0);
        gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
                dev->common.module);
        err = mapBuffer(module, hnd);
        if (err == 0) {
            *pHandle = hnd;
        }   
    }   
      
    return err;
}

在創(chuàng)建好ashmem后号醉,并做好地址映射后反症,輸出明明是GraphicBuffer中的handle,也就是buffer_handle_t的,為何在這里被賦值成private_handle_t的地址了呢畔派?

struct private_handle_t : public native_handle {
    enum {
        PRIV_FLAGS_FRAMEBUFFER = 0x00000001
    };
  
    // file-descriptors
    int     fd;
    // ints 
    int     magic;
    int     flags;
    int     size;
    int     offset;

    // FIXME: the attributes below should be out-of-line
    uint64_t base __attribute__((aligned(8)));
    int     pid;

    static inline int sNumInts() {
        return (((sizeof(private_handle_t) - sizeof(native_handle_t))/sizeof(int)) - sNumFds);
    }
    static const int sNumFds = 1;
    static const int sMagic = 0x3141592;
    
    private_handle_t(int fd, int size, int flags) : fd(fd), magic(sMagic), flags(flags), size(size), offset(0),
        base(0), pid(getpid())
    {
        version = sizeof(native_handle);
        numInts = sNumInts();
        numFds = sNumFds; 
    }           
    ~private_handle_t() {
        magic = 0; 
    }       
    ...
};

原來private_handle_t繼承于native_handle, 僅僅是將private_handle_t的地址保存到 GraphicBuffer中的handle而已铅碍,但是能反過來通過handle找到private_handle_t么?答案是可以的线椰, 原因是native_handle_t最后定義了一個int data[0],

private_handle_t內(nèi)存結(jié)構(gòu)

其中data的地址與fd的地址是一樣的胞谈,這里利用了GCC的一個trick. 而native_handle_t里保存的就是data所指地址的長度。

來看下numInts的賦值

    static inline int sNumInts() {
        return (((sizeof(private_handle_t) - sizeof(native_handle_t))/sizeof(int)) - sNumFds);
    }

可以看出numInts被賦值為除去native_handle_t的private_handle_t自己的成員變量的個數(shù),但是這里減1了烦绳,這個在GraphicBuffer的flatten與unflatten會一一對應起來卿捎,(減1是減掉了fd).


由于GraphicBuffer繼承于Flattenable, Flattenable的數(shù)據(jù)類型可以通過Binder傳遞,Parcel中有對Flattenable數(shù)據(jù)的處理.

status_t Parcel::write(const FlattenableHelperInterface& val)
{
    status_t err;

    // size if needed
    const size_t len = val.getFlattenedSize();
    const size_t fd_count = val.getFdCount();
    ...
    err = this->writeInt32(len);
    err = this->writeInt32(fd_count);

    // payload
    void* const buf = this->writeInplace(pad_size(len)); //獲得長度為len的buffer

    int* fds = NULL;
    if (fd_count) {
        fds = new (std::nothrow) int[fd_count];
    }

    err = val.flatten(buf, len, fds, fd_count);
    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
        err = this->writeDupFileDescriptor( fds[i] );
    }

    if (fd_count) {
        delete [] fds;
    }

    return err;
}

來看下GraphicBuffer中的getFlattenedSize

size_t GraphicBuffer::getFlattenedSize() const {
    return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
}

getFlattenedSize 這里多加了11個字節(jié)径密,也是特定的午阵,這11個字節(jié)用來存放一些width/height, 頭部等等數(shù)據(jù)享扔,然后就是private_handle_t中的變量.

接著來看下GraphicBuffer的flatten函數(shù)

status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
    size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
    if (size < sizeNeeded) return NO_MEMORY;

    size_t fdCountNeeded = GraphicBuffer::getFdCount();
    if (count < fdCountNeeded) return NO_MEMORY;

    int32_t* buf = static_cast<int32_t*>(buffer);
    buf[0] = 'GBFR';
    buf[1] = width;
    buf[2] = height;
    buf[3] = stride;
    buf[4] = format;
    buf[5] = usage;
    buf[6] = static_cast<int32_t>(mId >> 32);
    buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
    buf[8] = static_cast<int32_t>(mGenerationNumber);
    buf[9] = 0;
    buf[10] = 0;

    if (handle) {
        buf[9] = handle->numFds;
        buf[10] = handle->numInts;
        memcpy(fds, handle->data,
                static_cast<size_t>(handle->numFds) * sizeof(int));
        memcpy(&buf[11], handle->data + handle->numFds,
                static_cast<size_t>(handle->numInts) * sizeof(int));
    }

    buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
    size -= sizeNeeded;
    if (handle) {
        fds += handle->numFds;
        count -= static_cast<size_t>(handle->numFds);
    }

    return NO_ERROR;
}

flatten函數(shù)是將SurfaceFlinger中的GraphicBuffer中成員變量flatten到Buffer中趟庄,用于 Binder 傳輸?shù)綉眠M程中去。

從flatten可以看出伪很,在獲得flatten size的時間多加的11個int型用于儲存相關(guān)的數(shù)據(jù)戚啥, 然后將private_handle_t中fd的拷到fds中,再將private_handle_t中其它的數(shù)據(jù)拷到buffer+11開始的地址中去, 那么整個buffer的值就是

buffer的值

同理 unflatten 與flatten相反锉试,就是將buffer內(nèi)存塊儲存的值轉(zhuǎn)換到應用進程的GraphicBuffer中

注意猫十,這里面有個非常重要的字段 fd, fd 是 surfacelinger 中的文件描述符,當將GraphicBuffer傳遞給App進程里呆盖, Binder會在內(nèi)核中將SurfaceFlinger進程中的fd所指向的文件描述塊賦值給App進程中的新的fd, 具體可以參考binder ---傳遞文件描述符.

最后還有一個非常非常重要的App進程去映射Ashmem匿名內(nèi)存拖云,具體是在GraphicBuffer的unflatten中完成的

GraphicBuffer::unflatten
  -> GraphicBufferMapper::registerBuffer
    -> gralloc_module_t::registerBuffer
      -> gralloc_register_buffer
        -> gralloc_map -> mmap

建立好GraphicBuffer中的private_handle_t結(jié)構(gòu)體,至此应又,App進程就和SurfaceFlinger映射到同一塊內(nèi)存了宙项,App也就可以往這塊內(nèi)存里寫內(nèi)容了。

盜用Android圖形緩沖區(qū)映射過程源碼分析

圖形緩沖區(qū)映射

三株扛、BufferQueue之Producer(由Surface通過Binder線程調(diào)用)

Producer_Consumer

3.1 SurfaceFlinger進程預分配GraphicBuffer

App進程通過Binder線程向SurfaceFlinger進程發(fā)起allocateBuffer請求尤筐,Android硬件渲染環(huán)境初始化中提到了allocateBuffer函數(shù),它的作用是預先分配Buffer, 這樣可以避免在渲染時分配帶來的延時洞就。 allocateBuffer最終會觸發(fā)BufferQueueProducer去allocateBuffer

void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
        PixelFormat format, uint32_t usage) {
    while (true) {
        {
            ...
            //這里 newBufferCount 為 3
            newBufferCount = mCore->mFreeSlots.size(); 
            ...
            mCore->mIsAllocating = true; //bool類型指明當前正在分配GraphicBuffer
        } 

        Vector<sp<GraphicBuffer>> buffers;
        for (size_t i = 0; i <  newBufferCount; ++i) {
            status_t result = NO_ERROR;
            //創(chuàng)建GraphicBuffer
            sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
                    allocWidth, allocHeight, allocFormat, allocUsage, &result));  
            buffers.push_back(graphicBuffer);
        }
        { // Autolock scope
            for (size_t i = 0; i < newBufferCount; ++i) {
                if (mCore->mFreeSlots.empty()) {
                    continue;
                }
                auto slot = mCore->mFreeSlots.begin();
                mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
                mSlots[*slot].mGraphicBuffer = buffers[i];
                mSlots[*slot].mFence = Fence::NO_FENCE;
                //此時分配了GraphicBuffer, 放入mFreeBuffers中 
                mCore->mFreeBuffers.push_front(*slot);
                //已經(jīng)分配了GraphicBuffer, 那么mFreeSlots就不再保存這些slot
                mCore->mFreeSlots.erase(slot);
            }

            mCore->mIsAllocating = false;
        } // Autolock scope
    }
}
SurfaceFlinger進程預分配的Buffer

3.2 dequeueBuffer

3.2.1 App進程通過Binder線程向SurfaceFlinger進程發(fā)起dequeueBuffer請求

syncFrameState()
  -> eglMakeCurrent 
    -> eglMakeCurrent(OEM EGL) 
      -> Surface::hook_dequeueBuffer() 
        -> Surface::dequeueBuffer() 
            -> BpGraphicBufferProducer::dequeueBuffer()
            -> BpGraphicBufferProducer::requestBuffer()

上述的dequeueBuffer的調(diào)用過程在不同的平臺實現(xiàn)可能不同盆繁,不必太深究

3.2.2 SurfaceFlinger進程中的dequeueBuffer

App進程發(fā)起B(yǎng)pGraphicBufferProducer::dequeueBuffer的請求,在SurfaceFlinger進程會觸發(fā)dequeueBuffer

status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
        sp<android::Fence> *outFence, uint32_t interval,
        uint32_t width, uint32_t height, PixelFormat format, uint32_t usage) {
    { // Autolock scope
        int found = BufferItem::INVALID_BUFFER_SLOT;
        //找到一個BufferSlot
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,&found);
        }

        if (mCore->mSharedBufferSlot != found) {
            //現(xiàn)在的狀態(tài)是DEQUEUE, 所以將slot插入到mActiveBuffers中
            mCore->mActiveBuffers.insert(found);
        }
        *outSlot = found;

        attachedByConsumer = mSlots[found].mNeedsReallocation;
        mSlots[found].mNeedsReallocation = false;
        //設置為DEQUEUE
        mSlots[found].mBufferState.dequeue();

        //檢查下是否需要重新分配GraphicBuffer
        if ((buffer == NULL) ||
                buffer->needsReallocation(width, height, format, usage))
        {
            returnFlags |= BUFFER_NEEDS_REALLOCATION;
        } else {
            mCore->mBufferAge =
                    mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
        }
        ...
    } // Autolock scope

    //如果需要重新分配的話旬蟋,進入該分支
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        status_t error;
        sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
                width, height, format, usage, &error));
        { // Autolock scope
            if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
            }
            ...
            mCore->mIsAllocating = false;
            mCore->mIsAllocatingCondition.broadcast();
        } // Autolock scope
    }
    return returnFlags;
}

dequeueBuffer 主要做了以下幾件事情

    1. 獲得一個可用的BufferSlot
      通過waitForFreeSlotThenRelock函數(shù)從mFreeBuffers(getFreeBufferLocked)或者從mFreeSlots(getFreeSlotLocked)獲得一個BufferSlot,
    1. 將BufferSlot插入到mActiveBuffers中
    1. 設置BufferSlot的狀態(tài)為 DEQUEUE
      狀態(tài)變化 FREE -> DEQUEUE
  • 4.檢查是否需要重新分配GraphicBuffer
    由于可能是從1中的mFreeBuffers中獲得可用的BufferSlot, 而該BufferSlot已經(jīng)分配過GraphicBuffer了油昂,但是此時dequeueBuffer需要的width/height/format/usage與該BufferSlot不對應,此時就需要重新分配一個新的GraphicBuffer

    1. 將最后的BufferSlot返回給 APP進程中的Surface
deququeBuffer

如圖所示倾贰,dequeueBuffer找到了第三個BufferSlot.

3.3 requestBuffer

status_t BnGraphicBufferProducer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case REQUEST_BUFFER: {
            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
            int bufferIdx   = data.readInt32();
            sp<GraphicBuffer> buffer; //局部變量
            int result = requestBuffer(bufferIdx, &buffer);  
            reply->writeInt32(buffer != 0);
            if (buffer != 0) {
                reply->write(*buffer);
            }
            reply->writeInt32(result);
            return NO_ERROR;
        }
       ....
    }
}

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf)
{
    ...
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

requestBuffer很簡單冕碟,App進程請求SurfaceFlinger將slot對應的BufferSlot中的GraphicBuffer傳給App進程,由2,2節(jié)匆浙,當Surface去dequeueBuffer時安寺,找到了Slot2中的BufferSlot,requestBuffer就將該BufferSlot中的GraphicBuffer傳遞給App進程的Surface, 詳細可以參見 2.2.2 GraphicBuffer吞彤。

3.4 queueBuffer

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    //input是Surface傳過來的我衬,input是flattenable的,這個可以看下前面的GraphicBuffer
    input.deflate(&timestamp, &isAutoTimestamp, &autoTimestamp, &dataSpace, &crop, &scalingMode,
            &transform, &interval, &fence, &stickyTransform);

    sp<IConsumerListener> frameAvailableListener;
    sp<IConsumerListener> frameReplacedListener;
    BufferItem item;
    { // Autolock scope
        //獲得slot中的GrahicBuffer
        const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);

        mSlots[slot].mFence = fence;
        //狀態(tài)從DEQUQUE -> QUEUE
        mSlots[slot].mBufferState.queue();

        //將slot對應的BufferSlot的數(shù)據(jù)拷貝到BufferItem中
        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        item.mSlot = slot;

        //mQueue是一個FIFO隊列饰恕,里面保存的queued buffers
        if (mCore->mQueue.empty()) {
            mCore->mQueue.push_back(item);
            frameAvailableListener = mCore->mConsumerListener;
        } else {
            //當前隊列不為空挠羔,那么說明Consumer還沒消費掉,既然有新的Buffer來埋嵌,那舊的理論上是可以不用再顯示了
            const BufferItem& last = mCore->mQueue.itemAt(mCore->mQueue.size() - 1);
            if (last.mIsDroppable) { //是否是可以Drop掉
                if (!last.mIsStale) { 
                    //如果已經(jīng)過期了破加,則修改一些該BufferSlot的一些狀態(tài),
                    //比如將它從 mActiveBuffers中移出雹嗦,加入到mFreeBuffers中
                    ...
                }
                
                // Overwrite the droppable buffer with the incoming one
                //用最新的BufferItem替換最后那個
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;
            } else { 
                //如果最后那個是不能Drop的范舀,那新來的Buffer也只能放在最后了。
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;
            }
        }
        //output是傳給App的
        output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
                mCore->mTransformHint,
                static_cast<uint32_t>(mCore->mQueue.size()));

    } // Autolock scope

    // Don't send the GraphicBuffer through the callback, and don't send
    // the slot number, since the consumer shouldn't need it
    //看注釋是說Consumer不會用到slot值了罪,以及GraphicBuffer锭环,???
    item.mGraphicBuffer.clear();
    item.mSlot = BufferItem::INVALID_BUFFER_SLOT;

    { // scope for the lock
        if (frameAvailableListener != NULL) {
            frameAvailableListener->onFrameAvailable(item); // 回調(diào) frame available
        } else if (frameReplacedListener != NULL) {
            frameReplacedListener->onFrameReplaced(item);
        }
    }
    ...
}

當新的一幀準備好后(queueBuffer),這時就要通知 Consumer去消費了泊藕,這時調(diào)用的是 BufferQueueCore::mConsumerListener -> onFrameAvailable

ConsumerListener

如圖所示辅辩,最終會調(diào)用到Layer的onFrameAvailable中

void Layer::onFrameAvailable(const BufferItem& item) {
    { // Autolock scope
        //push到 mQueueItems中去
        mQueueItems.push_back(item);
        android_atomic_inc(&mQueuedFrames); 
        ...
    }

    mFlinger->signalLayerUpdate();
    if (mQueuedFrames>1) {  //如果有兩個以上的Frames, 表示已經(jīng)mQueueItems已經(jīng)滿了
        queueLengthState = QUEUE_IS_FULL; 
    }
}

void SurfaceFlinger::signalLayerUpdate() {
    mEventQueue.invalidate();  
}

mQueuedFrames表示當前已經(jīng)Queued 的 Frame的個數(shù)

接下來看下 MessageQueue::invalidate(),

#define INVALIDATE_ON_VSYNC 1
void MessageQueue::invalidate() {
#if INVALIDATE_ON_VSYNC
    mEvents->requestNextVsync();
#else
    mHandler->dispatchInvalidate(); 
#endif
}

從MessageQueue中的invalidate可以看出來,會直接調(diào)用 mEvents的requestNextVsync()
那這個mEvents是什么呢娃圆? 具體參考 Android SurfaceFlinger SW Vsync模型, mEvents也就是SF EventThread里創(chuàng)建的一個EventThread::Connection

鏈接 Android SurfaceFlinger SW Vsync模型里面的 vsync信號產(chǎn)生圖玫锋, 如圖中 1、3讼呢、3_1 所示

vsync信號產(chǎn)生

requestNextVsync經(jīng)過SF EventThead后撩鹿,最后在MessageQueue中的eventReceiver又會調(diào)用 mHandler->dispatchInvalidate()

mHandler->dispatchInvalidate()
  ->SurfaceFlinger::onMessageReceived()
    -> handleMessageInvalidate()
    -> signalRefresh()

看到這里, dispatchInvalidate分為兩個步聚了悦屏,一個是handleMessageInvalidate() 這個是Layer對GraphicBuffer做一些處理节沦,當處理完成后,就通過HWConsumer去合成buffer, 然后顯示了

四础爬、BufferQueue之Consumer (SurfaceFlinger EventThread里消費數(shù)據(jù))

當Layer通過onFrameAvailable回調(diào)并通過requestNextVsync去請求一個VSYNC同步信號去喚醒Consumer去消費掉新數(shù)據(jù)散劫。 最后在SF EventThread里收到INVALDATE信息,處理完INVALIDATE信息后又signalRefresh觸發(fā) REFRESH信息幕帆,將APP要顯示的內(nèi)容刷新到屏幕上

void SurfaceFlinger::onMessageReceived(int32_t what) {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::INVALIDATE: {
            bool refreshNeeded = handleMessageTransaction();
            refreshNeeded |= handleMessageInvalidate();
            refreshNeeded |= mRepaintEverything;
            if (refreshNeeded) {
                // Signal a refresh if a transaction modified the window state,
                // a new buffer was latched, or if HWC has requested a full
                // repaint
                signalRefresh();
            }
            break;
        }
        case MessageQueue::REFRESH: {
            handleMessageRefresh();
            break;
        }
    }
}

4.1 handleMessageTransaction

SurfaceFlinger和Layer分別處理事務, 分別跟新Drawing State, 這個Drawing 狀態(tài)用在后續(xù)的操作中获搏,作為當前繪制幀的狀態(tài)存在
SurfaceFlinger事務處理

4.2 handleMessageInvalidate

從 QUEUED 的Buffer中找到最合適的Buffer用于后面合成顯示
SurfaceFlinger之handlePageFlip

4.3 handleMessageRefresh

對Layer進行合成,刷新到屏幕上
SurfaceFlinger之Refresh

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末失乾,一起剝皮案震驚了整個濱河市常熙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碱茁,老刑警劉巖裸卫,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纽竣,居然都是意外死亡墓贿,警方通過查閱死者的電腦和手機茧泪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聋袋,“玉大人队伟,你說我怎么就攤上這事∮睦眨” “怎么了嗜侮?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啥容。 經(jīng)常有香客問我锈颗,道長,這世上最難降的妖魔是什么咪惠? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任击吱,我火速辦了婚禮,結(jié)果婚禮上遥昧,老公的妹妹穿的比我還像新娘姨拥。我一直安慰自己,他們只是感情好渠鸽,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布叫乌。 她就那樣靜靜地躺著,像睡著了一般徽缚。 火紅的嫁衣襯著肌膚如雪憨奸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天凿试,我揣著相機與錄音排宰,去河邊找鬼。 笑死那婉,一個胖子當著我的面吹牛板甘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播详炬,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼盐类,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了呛谜?” 一聲冷哼從身側(cè)響起在跳,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤隐岛,失蹤者是張志新(化名)和其女友劉穎齐帚,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沪羔,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蔫饰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年篓吁,在試婚紗的時候發(fā)現(xiàn)自己被綠了驰贷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片次兆。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡锹锰,死狀恐怖园蝠,靈堂內(nèi)的尸體忽然破棺而出陪汽,到底是詐尸還是另有隱情挚冤,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布颊艳,位于F島的核電站忘分,受9級特大地震影響窥浪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜啊终,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望傲须。 院中可真熱鬧蓝牲,春花似錦、人聲如沸泰讽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽已卸。三九已至佛玄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間累澡,已是汗流浹背梦抢。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愧哟,地道東北人奥吩。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓哼蛆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親霞赫。 傳聞我的和親對象是個殘疾皇子腮介,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

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