美文网首页
Android 图形系统(8)---- Bufferqueue

Android 图形系统(8)---- Bufferqueue

作者: 特立独行的佩奇 | 来源:发表于2023-04-07 19:32 被阅读0次

    BufferQueue

    BufferQueue要解决的是生产者和消费者的同步问题,应用程序产生画面,SurfaceFlinger 消费画面;SurfaceFlinger 生成画面而HWC service 消费画面;用来存储这些画面的区域我们称为缓冲区,为此需要按照下面需求设计:

    1. 需要有缓冲区供生产者消费者使用
    2. 生产者生产完成需要能够通知到消费者
    3. 生产者一般为app,消费者是 sf,因此需要拷贝跨进程通信的问题

    下面我们以应用程序为生产者,SurfaceFlinger 为消费者为例,了解一下BufferQueue 的内部设计

    BufferState 的切换

    在BufferQueue 的设计中,Buffer 存在下面几种状态:
    FREE:表示该Buffer 是空闲的,可以给到应用程序,由应用程序来绘图
    DEQUEUED:表示Buffer 的控制权已经交给了应用程序侧,这个状态下应用程序可以在上面绘图了
    QUEUED:表示该Buffer已经由应用程序绘图完成,Buffer 的控制权已经回到SurfaceFlinger 了
    ACQUIRED:表示该Buffer 已经交给 HWC service去合成了,这个时候控制权已经给到HWC service了
    Buffer 的初始状态是Free

    1. 当生产者通过dequeueBuffer 来申请Buffer 成功时,buffer 的状态变为 QEQUEUED
    2. 应用程序绘制完成后通过queueBuffer 把BufferState 状态改为 QUEUED 状态
    3. SurfaceFlinger 通过 acquiredBuffer 把 Buffer 拿去给 HWC service合成,这时的Buffer 变为 ACQUIRED 状态
    4. 合成完成后通过 release buffer 把 Buffer 状态改为FREE状态,
      状态切换图如下图所示:


      BBQ_base.jpg

    从时间轴上一个Buffer 的变化过程是:
    FREE->DEQUEUED->QUEUED->ACQUIRED->FREE

    BBQ_timeline.jpg

    BufferSlot

    每一个应用程序图层在SurfaceFlinger 里称为一个Layer,每个Layer都有一个独特的BufferQueue,每个 BufferQueue都会有多个Buffer,目前Android 系统上单图层最多支持 64个Buffer

    BufferSlot 的定义如下:
    framework/native/libs/gui/include/gui/BufferQueueDefs.h

    namespace android {
        class BufferQueueCore;
    
        namespace BufferQueueDefs {
            typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
        } // namespace BufferQueueDefs
    } // namespace android
    

    SlotsType 被 typedef 定义成了数组类型,数组元素是BufferSlot,数组的大小为NUM_BUFFER_SLOTS(64)
    BufferSlot 是对GraphicBuffer 的封装,重要成员如下:

    struct BufferSlot {
    .....
        BufferState mBufferState;  //当前的 Buffer 状态,FREE/DEQUEUED/QUEUED/ACQUIRED
        sp<GraphicBuffer> mGraphicBuffer; // 代表了Buffer 存储空间
        uint64_t mFrameNumber; //表示这个slot被queued的编号,在应用调dequeueBuffer申请slot时会参考该值
        sp<Fence> mFence;
    }
    

    64 个 BufferSlot 可以分为两部分,usedslot(使用中的) 和 unusedlot(未使用中的)
    usedslot = active slot + unactive slot
    而usedslot又可以分为 active slot 和 unactive slot,处在 DEQUEUED,QUEUED,ACQUIRED状态的被称为active slot,剩下FREE状态的称为unactive Slots;
    所有activeSlots都是有人正在使用中的 slot,使用者可以是生产者也可以是消费者

    unactive slot = Free state slot = mFreeBuffers + mFreeSlots
    而Free 状态的slot 根据是否为其分配内存可以分为mFreeBuffers 和 mFreeSlots

    • mFreeBuffers:已经分配过内存的
    • mFreeSlots:没有分配过内存的
      如果从代码中看到mFreeSlots中拿出一个 Bufferslot 那说明这个Bufferslot 还没有配置过GraphicBuffer的,这个slot 可能第一次用到

    Buffer 的分配流程

    framework/native/libs/gui/Surface.cpp

    1. 应用第一次dequeueBuffer前会通过connect接口和SurfaceFlinger 连接
    int Surface::connect(
        int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
        ATRACE_CALL();
        ...
        int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
    
    }
    
    1. 调用 dequeueBuffer时会先调用requestBuffer
      result 标志第一次会带 BUFFER_NEEDS_REALLOCATION 标志
    int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
        ATRACE_CALL();//这里可以在systrace中看到
        ......
        //这里尝试去dequeueBuffer,因为这时SurfaceFlinger对应Layer的slot还没有分配buffer,这时SurfaceFlinger会回复的flag会有BUFFER_NEEDS_REALLOCATION
        status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, 
                                           reqFormat, reqUsage, &mBufferAge, 
                                            enableFrameTimestamps?&frameTimestamps:nullptr);
        ......
        if((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
            ......
            //这里检查到dequeueBuffer返回的结果里带有BUFFER_NEEDS_REALLOCATION标志就会发出一次requestBuffer
            result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
            ......
        }
        ......
    }
    
    1. 因为应用侧 Surface 对象中 包含的是 GraphicBufferProducer 的 binder client端对象,所以实际的调用是在
      Surfaceflinger 进程的 server 端 BufferQueueProducer.cpp,注意dequeueBuffer 实际返回的是一个空间 slot
    status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                                uint32_t width, uint32_t height, PixelFormat format,
                                                uint64_t usage, uint64_t* outBufferAge,
                                                FrameEventHistoryDelta* outTimestamps) {
        if ((buffer == NULL) ||
            buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))//检查是否已分配了GraphicBuffer
        {
            ......
            returnFlags |= BUFFER_NEEDS_REALLOCATION;//发现需要分配buffer,置个标记
        }
        ......
        if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
            ......
            //新创建一个新的GraphicBuffer给到对应的slot
            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                   width, height, format, BQ_LAYER_COUNT, usage,
                   {mConsumerName.string(), mConsumerName.size()});
            ......
                   mSlots[*outSlot].mGraphicBuffer = graphicBuffer;//把GraphicBuffer给到对应的slot
            ......
        }
        ......
        return returnFlags;//注意在应用第一次请求buffer, dequeueBuffer返回时对应的GraphicBuffer已经创建完成并给到了对应的slot上,但返回给应用的flags里还是带有BUFFER_NEEDS_REALLOCATION标记的
    }
    

    根据 client 端的调用,会继续调用到requestBuffer

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

    tips: 为什么不在 dequeueBuffer时,直接返回 GraphicBuffer 对象,而是返回一个空闲的 slot,再调用requestBuffer去获得一个 GraphicBuffer?
    因为这个接口调用太频繁了,比如在90FPS的设备上,一秒钟该接口要执行90次,太频繁了,而且这个信息只需要传递一次就可以了,如果每次这个接口都要带上GraphicBuffer的信息,传输了很多冗余数据,所以不如加入一个新的api(requestBuffer)来完成GraphicBuffer传递的事情

    GraphicBuffer 继承了 Parcel 对象,可以通过Binder跨进程传递

    相关文章

      网友评论

          本文标题:Android 图形系统(8)---- Bufferqueue

          本文链接:https://www.haomeiwen.com/subject/wtgpddtx.html