BLASTBufferQueue02-BufferQueueProducer核心操作

前言

BufferQueueProducer跟图形实际内容生产方连接,从BBQ队列向生产方提供空闲Graphic Buffer,并在生产方填充好数据后再返回给Buffer Queue。在整个工作过程中,BufferQueueProducer主要负责以下几个操作:

  • allocateBuffers:预分配Buffer;

  • dequeueBuffer:向图形内容生产方提供空闲Buffer,确切地说是BBQ队列中空闲Buffer的索引;

  • requesetBuffer:图形内容生产方和BBQ队列中的Buffer进行映射;

  • queueBuffer:将图形内容生产方填充好内容的Buffer及其所有权返回给BBQ队列;

  • cancelBuffer:当图形内容生产方不再需要填充Buffer时,通过该操作将Buffer所有权返回给BBQ队列;

  • attachBuffer:将图形内容生产方指定的Buffer关联到BBQ中对应的BufferSlot上,并返回BufferSlot索引值;

  • detachBuffer:将指定slot对应的BufferSlot重置回到未绑定GraphicBuffer的FREE状态。

本篇文章中,对以上操作中的四类主要操作进行分析总结。

一、allocateBuffers操作

allocateBuffers()方法用于为BBQ队列批量申请分配GraphicBuffer。

cpp 复制代码
// frameworks/native/include/gui/BufferQueueProducer.h

virtual void allocateBuffers(uint32_t width, uint32_t height,
        PixelFormat format, uint64_t usage) override;

BBQ中可在两个操作时分配Buffer:

  • dequeueBuffer时分配:当返回BufferSlot索引对应的BufferSlot对象未绑定GraphicBuffer、或者由于参数发生变化需要重新分配GraphicBuffer时,会申请分配;
  • 应用渲染线程预分配:通过allocateBuffers在创建完成BBQ后预先根据队列配置参数批量分配,减少dequeueBuffer操作的耗时。

在初始化GraphicBuffer对象时,会通过Gralloc图形内存分配器完成分配。

应用进程在ViewRootImpl中创建完成BBQ后,进行预分配GraphicBuffer的操作如下:

cpp 复制代码
// frameworks/base/core/java/android/view/ViewRootImpl.java

private void performTraversals() {
    ......
    // 预分配Buffer
    mAttachInfo.mThreadedRenderer.allocateBuffers();
}

经过一系列调用,在BBQSurface中,开启子线程并调用BufferQueueProducer::allocateBuffers()方法进行预分配:

cpp 复制代码
// frameworks/native/libs/gui/BLASTBufferQueue.cpp

class BBQSurface : public Surface {
    ......
    void allocateBuffers() override {
        uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
        uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
        auto gbp = getIGraphicBufferProducer();
        // 开启子线程,通过gbp申请分配Graphic Buffer
        std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
                      reqFormat=mReqFormat, reqUsage=mReqUsage] () {
            gbp->allocateBuffers(reqWidth, reqHeight,
                                 reqFormat, reqUsage);

        }).detach();
    }

BufferQueueProducer::allocateBuffers()方法如下:

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
        PixelFormat format, uint64_t usage) {

    // width、height未指定时,使用默认size
    const bool useDefaultSize = !width && !height;
    // 每次申请一个,循环申请,直到mCore->mFreeSlots为空(达到最大buffer数)
    while (true) {
        size_t newBufferCount = 0;
        ......
        { // Autolock scope
            std::unique_lock<std::mutex> lock(mCore->mMutex);
            // 如果当前mCore->mIsAllocating为true,说明处于正在分配Buffer流程中,
            // 此时进入等待状态,直到上次申请流程结束后将mCore->mIsAllocating置为false时,
            // 通过notify进行唤醒继续
            // mCore->mIsAllocatingCondition锁用于分配Buffer过程和其他流程间的控制
            mCore->waitWhileAllocatingLocked(lock);
            // mCore->mAllowAllocation为false表示不允许为该队列分配Buffer
            if (!mCore->mAllowAllocation) {
                BQ_LOGE("allocateBuffers: allocation is not allowed for this "
                        "BufferQueue");
                return;
            }
            // 如果mCore->mFreeSlots为空,说明已经申请分配完成了最大Buffer数量的GraphicBuffer,
            // 此时无法申请新的Buffer.
            // mCore->mFreeSlots保存还没有绑定GraphicBuffer的空闲BufferSlot在mSlots列表中
            // 的索引值, 非空说明还有BufferSlot未和GraphicBuffer绑定
            newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1;
            if (newBufferCount == 0) {
                return;
            }
            ......
            
            // 表示正在申请分配GraphicBuffer中,该方法结尾重置为false
            mCore->mIsAllocating = true;
        } // Autolock scope

        // 正式进行GraphicBuffer申请
        Vector<sp<GraphicBuffer>> buffers;
        // newBufferCount最大为1,该循环只执行1次
        for (size_t i = 0; i < newBufferCount; ++i) {
            // 申请分配GraphicBuffer
            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                    allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
                    allocUsage, allocName);

            status_t result = graphicBuffer->initCheck();
            ......
            // 申请的GraphicBuffer保存到列表中
            buffers.push_back(graphicBuffer);
        }

        { // Autolock scope
            ......
            
            for (size_t i = 0; i < newBufferCount; ++i) {
                .......
                // 取出mCore->mFreeSlots第一个索引元素, 将mSlots中对应BufferSlot绑定该Buffer
                auto slot = mCore->mFreeSlots.begin();
                // 清除mSlots[*slot]上的旧数据
                mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
                // 将GraphicBuffer赋给mSlots[*slot].mGraphicBuffer
                mSlots[*slot].mGraphicBuffer = buffers[i];
                // 初始化一个无效Fence对象
                mSlots[*slot].mFence = Fence::NO_FENCE;

                // 将索引值存储在mCore->mFreeBuffers中,
                // mCore->mFreeBuffers用于存放已申请完成Buffer但未被使用的BufferSlot数组索引值
                // 或者说用于存放处于空闲且已经绑定Buffer的BufferSlot数组索引值
                mCore->mFreeBuffers.push_front(*slot);

                // 将索引值从mCore->mFreeSlots中移除
                // 一旦mSlot[slot]绑定了Buffer,则需要将该slot从mCore->mFreeSlots移除,
                // 保存到mCore->mFreeBuffers中
                mCore->mFreeSlots.erase(slot);
            }
            // 分配过程结束,唤醒等待线程(如有)
            mCore->mIsAllocating = false;
            mCore->mIsAllocatingCondition.notify_all();

            // mDequeueWaitingForAllocation为true表示dequeueBuffer操作在等待申请过程的完成
            // dequeueBuffer继续后,会唤醒mDequeueWaitingForAllocationCondition阻塞线程
            while (mDequeueWaitingForAllocation) {
                mDequeueWaitingForAllocationCondition.wait(lock);
            }
        } // Autolock scope
    } // while true
}

这里再对以上方法中使用到的两个条件锁和两个容器进行下说明。

1.1、mCore->mIsAllocatingCondition锁

mCore->mIsAllocatingCondition锁用于申请GraphicBuffer流程和其他操作的同步,当mCore->mIsAllocating为true时,表示正在进行申请分配Buffer操作,此时会通过该锁进入等待状态,直到上次申请分配流程完成后,mCore->mIsAllocating会重置为false,然后唤醒此次等待流程继续执行。

1.2、mDequeueWaitingForAllocationCondition锁

mDequeueWaitingForAllocationCondition锁用于allocateBuffersdequeueBuffer操作的同步。

如果RenderThread线程在执行dequeueBuffer操作时,线程A正在进行allocateBuffers操作,此时在dequeueBuffer中,会首先将mDequeueWaitingForAllocation设置为true,然后执行mIsAllocatingCondition.wait()进入阻塞状态;

allocateBuffers中每完成一个Buffer分配后,会先执行mIsAllocatingCondition.notify_all()唤醒被Block线程,然后根据mDequeueWaitingForAllocation是否为true决定是否通过mDequeueWaitingForAllocationConditionBlock当前线程。

通过这两个锁,即保证了数据的同步,而且还降低了dequeueBuffer的延时。

1.3、mCore->mFreeSlots列表

mCore->mFreeSlots数组用于存放当前未绑定GraphicBuffer的BufferSlot对象在mCore->mSlots数组中的索引值。

在初始化BufferQueueCore时就会对mCore->mFreeSlots进行填充值,allocateBuffers操作时,根据该数组是否为空确定是否申请分配新的Buffer,为空时说明已全部绑定了GraphicBuffer,将不会申请新Buffer。

allocateBuffers时,每申请一个Buffer,就会从mCore->mFreeSlots中按照顺序取出一个slot,并将该slot从mCore->mFreeSlots中移除。

1.4、mCore->mFreeBuffers列表

mCore->mFreeBuffers列表用于存放已完成GraphicBuffer绑定、但未被使用(处于FREE状态)的BufferSlot对象在mCore->mSlots数组中的索引值(BufferSlot::mGraphicBuffer != nullptr)。

allocateBuffers时,从mCore->mFreeSlots中按顺序取出索引slot,然后将新分配的GraphicBuffer对象赋值给mSlots[slot].mGraphicBuffer,并将slot保存在mCore->mFreeBuffers中,意味着当前BBQ中已经有FREE且可用的GraphicBuffer。

allocateBuffer操作过程中BBQ内部变化表示如下:

  • 刚创建完成BBQ时,BufferQueueCore中mCore->mSlots数组中的BufferSlot都没有和GraphicBuffer关联;
  • 进行allocateBuffers后,每成功分配一个GraphicBuffer,mCore->mFreeSlots数组元素逐次减一,mCore->mFreeBuffers列表逐次增一,直到mCore->mFreeSlots为空。

目前BBQ默认最大Buffer数量4个,因此在初始化时,会申请4个GraphicBuffer。

二、dequeueBuffer操作

dequeueBuffer操作为图形内容生产方获取一个Graphic Buffer的所有权。

在BufferQueueProducer中,会将mCore->mSlots数组中空闲BufferSlot对象的索引值通过slot参数返回。

返回slot所在BufferSlot对象此时可能没有跟具体的GraphicBuffer绑定,这种情况时,客户端需要执行requestBuffer操作来重新分配新的GraphicBuffer完成绑定。

cpp 复制代码
// frameworks/native/include/gui/IGraphicBufferProducer.h
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
                               PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
                               FrameEventHistoryDelta* outTimestamps) = 0;

参数

  • slot:返回给图形内容生产方的GraphicBuffer在Buffer队列中的索引值;

  • fence:跟GraphicBuffer关联的Fence对象,作为acquire Fence监听GPU是否完成绘制,Fence::NO_FENCE表示初始无效Fence对象,会由图形内容生产方创建;

  • w/h:获取GraphicBuffer的宽和高,为0时使用BBQ中的默认值;

  • format:获取Graphic Buffer的格式,为0时使用BBQ中的默认值;

  • usage:指定Graphic Buffer使用方式,GRALLOC_USAGE_HW_RENDER表示GB可以被当做OpenGL ES的渲染目标;

  • outBufferAge:自完成上一次queueBuffer后经过的帧数;

  • outTimestamps:用于帧数据统计。

返回值

dequeueBuffer返回值是一个可以包含不同标记的int值,当成功获得空闲Buffer时,返回值为非负数:

  • NO_ERROR:默认返回值;
  • BUFFER_NEEDS_REALLOCATION:表示图形生产方需要立即执行requestBuffer重新分配GraphicBuffer;

dequeueBuffer失败时,返回负数:

  • NO_INIT:表示该BBQ已经废弃,与Consumer断开连接;
  • BAD_VALUE:参数错误、或给Shared Buffer Slot重新分配Buffer时返回;
  • INVALID_OPERATION:处于DEQUEUED状态的Buffer数量大于等于最大dequeue buffer数量时返回(mCore->mMaxDequeuedBufferCount);
  • WOULD_BLOCK:当没有空闲Buffer时,默认进入阻塞状态(同步模式)直到有空闲Buffer时才返回。如果是异步模式,则会返回WOULD_BLOCK;
  • TIMED_OUT:阻塞时间超过dequeue timeout还未获得空闲Buffer时返回。

该方法如下:

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

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) {
    ......
    { // Autolock scope
        // 如果mCore->mFreeBuffers中没有可用Buffer,且正在进行申请Buffer流程,则在这里进行等待;
        // 申请Buffer完成后唤醒,同时通过mDequeueWaitingForAllocation通知allocateBuffer
        // dequeueBuffer不再等待
        if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
            mDequeueWaitingForAllocation = true;
            mCore->waitWhileAllocatingLocked(lock);
            mDequeueWaitingForAllocation = false;
            mDequeueWaitingForAllocationCondition.notify_all();
        }
        ......
        // while循环中获取空闲BufferSlot索引
        int found = BufferItem::INVALID_BUFFER_SLOT;
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
            // waitForFreeSlotThenRelock中获取空闲BufferSlot索引
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
            if (status != NO_ERROR) {
                return status;
            }
            ......
            const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);

            // Buffer参数变化后的重新分配操作
            // mCore->mAllowAllocation为false时,waitForFreeSlotThenRelock返回的Slot对应
            // BufferSlot肯定已绑定GraphicBuffer,这时需要判断Buffer参数是否发生变化
            // 若发生变化,清空该BufferSlot,并重新执行waitForFreeSlotThenRelock
            if (!mCore->mAllowAllocation) {
                if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
                    ......
                    mCore->mFreeSlots.insert(found);
                    mCore->clearBufferSlotLocked(found);
                    found = BufferItem::INVALID_BUFFER_SLOT;
                    continue;
                }
            }
        }
        // 获取对应索引位置GraphicBuffer
        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
        //Shard Buffer mode的Buffer无法重新分配
        if (mCore->mSharedBufferSlot == found &&
                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
            return BAD_VALUE;
        }

        // 标记为ActiveBuffer
        if (mCore->mSharedBufferSlot != found) {
            mCore->mActiveBuffers.insert(found);
        }
        // 将索引赋值给outSlot
        *outSlot = found;
        // BufferSlot.mNeedsReallocation值为true时,说明该BufferSlot重新绑定了新GraphicBuffer,
        // 这时需要添加标记BUFFER_NEEDS_REALLOCATION通知Surface,防止使用过时GraphicBuffer
        attachedByConsumer = mSlots[found].mNeedsReallocation;
        // 重置mSlots[found].mNeedsReallocation
        mSlots[found].mNeedsReallocation = false;
        // 将BufferSlot.mBufferState状态设置为DEQUEUED状态
        mSlots[found].mBufferState.dequeue();

        // 如果found来自mCore->mFreeSlots,则Buffer为空,或Buffer需要reallocate时,
        // 重置BufferSlot对象中所有属性
        if ((buffer == nullptr) ||
                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
        {
            mSlots[found].mAcquireCalled = false;
            mSlots[found].mGraphicBuffer = nullptr;
            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;
            mCore->mIsAllocating = true;
            // 添加BUFFER_NEEDS_REALLOCATION标记
            returnFlags |= BUFFER_NEEDS_REALLOCATION;
        } else {
            // 用于记录最近一次dequeueBuffer返回Buffer的年龄,即自上次queueBuffer后经过的帧数,总帧数 - 该Buffer帧数
            mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
        }


        eglDisplay = mSlots[found].mEglDisplay;
        eglFence = mSlots[found].mEglFence;
        // 设置acquire Fence
        *outFence = (mCore->mSharedBufferMode &&
                mCore->mSharedBufferSlot == found) ?
                Fence::NO_FENCE : mSlots[found].mFence;
        // 重置BufferSlot对象mEglFence和mFence
        mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
        mSlots[found].mFence = Fence::NO_FENCE;
        ......
        // 发起mConsumerListener->onFrameDequeued回调
        if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
            if (mCore->mConsumerListener != nullptr) {
                mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
            }
        }
    }
    // 执行reallocate Buffer操作
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        // 通过new GraphicBuffer申请Buffer
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                width, height, format, BQ_LAYER_COUNT, usage,
                {mConsumerName.string(), mConsumerName.size()});

        status_t error = graphicBuffer->initCheck();

        { // Autolock scope
            std::lock_guard<std::mutex> lock(mCore->mMutex);

            if (error == NO_ERROR && !mCore->mIsAbandoned) {
                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                // 将新GraphicBuffer跟BufferSlot绑定
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
                if (mCore->mConsumerListener != nullptr) {
                    mCore->mConsumerListener->onFrameDequeued(
                            mSlots[*outSlot].mGraphicBuffer->getId());
                }
            }
            // 分配Buffer结束,唤醒mIsAllocatingCondition
            mCore->mIsAllocating = false;
            mCore->mIsAllocatingCondition.notify_all();
            ......
        } // Autolock scope
    }
    // 设置BUFFER_NEEDS_REALLOCATION标记
    if (attachedByConsumer) {
        returnFlags |= BUFFER_NEEDS_REALLOCATION;
    }
    ......

    return returnFlags;
}

以上方法中,主要有以下几个较为关键的内容:

  1. 通过waitForFreeSlotThenRelock()方法获得空闲BufferSlot索引;
  2. 当Buffer参数等发生变化需要重新分配时,进行reallocateBuffer的操作;
  3. 将获得索引值添加到mCore->mActiveBuffers列表中,同时通过mSlots[found].mBufferState.dequeue()将该BufferSlot状态设置为DEQUEUED状态。

2.1、waitForFreeSlotThenRelock()获取空闲BufferSlot索引

dequeueBuffer操作中,实际在waitForFreeSlotThenRelock()方法中获取空闲BufferSlot索引,默认场景下(同步模式),当获取不到空闲BufferSlot索引时,将阻塞当前线程,直到获得空闲BufferSlot索引:

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {

    bool tryAgain = true;
    while (tryAgain) {
        // 如果BufferQueue已废弃,直接返回NO_INIT,disconnect()后将废弃
        if (mCore->mIsAbandoned) {
            return NO_INIT;
        }

        // 统计DEUEUED和ACQUIRED的Buffer个数
        int dequeuedCount = 0;
        int acquiredCount = 0;
        for (int s : mCore->mActiveBuffers) {
            if (mSlots[s].mBufferState.isDequeued()) {
                ++dequeuedCount;
            }
            if (mSlots[s].mBufferState.isAcquired()) {
                ++acquiredCount;
            }
        }
        
        // 获取BBQ最大Buffer数,默认为4个
        const int maxBufferCount = mCore->getMaxBufferCountLocked();
        // 当producer出现disconnect又重新connect场景时,Buffer最大数量如果发生调整,
        // 且mCore->mQueue > maxBufferCount时,会先等待mCore->mQueue中的完成使用和释放,
        // 避免出现溢出
        bool tooManyBuffers = mCore->mQueue.size()
                            > static_cast<size_t>(maxBufferCount);
        if (tooManyBuffers) {
            BQ_LOGV("%s: queue size is %zu, waiting", callerString,
                    mCore->mQueue.size());
        } else {
            // 如果有shared buffer存在,则直接返回Shared buffer slot
            if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
                    BufferQueueCore::INVALID_BUFFER_SLOT) {
                *found = mCore->mSharedBufferSlot;
            } else {
                if (caller == FreeSlotCaller::Dequeue) {
                    // 优先从mCore->mFreeBuffers列表中获取BufferSlot索引,
                    // 如果mCore->mFreeBuffers为空,则再从mCore->mFreeSlots中获取索引
                    // mCore->mFreeSlots中获得的Slot没有绑定Buffer, 需要重新分配
                    int slot = getFreeBufferLocked();
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else if (mCore->mAllowAllocation) {
                        *found = getFreeSlotLocked();
                    }
                } else {
                    // attachBuffer操作
                }
            }
        }

        // 没有获得空闲BufferSlot索引、或QUEUED状态Buffer数大于BBQ最大Buffer数时,
        // 在同步模式下会进入等待,直到获得slot
        tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
                   tooManyBuffers;
        if (tryAgain) {
            // 非阻塞模式(异步模式)时直接返回WOULD_BLOCK
            if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
                    (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
                return WOULD_BLOCK;
            }
            // 设置了dequeue timeout时,到达耗时时间后返回TIMED_OUT
            // 默认为-1, BBQ初始化时会设置为最大int值
            if (mDequeueTimeout >= 0) {
                std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
                        std::chrono::nanoseconds(mDequeueTimeout));
                if (result == std::cv_status::timeout) {
                    return TIMED_OUT;
                }
            } else {
                // 进入wait状态,直到有Buffer空出时唤醒
                mCore->mDequeueCondition.wait(lock);
            }
        }
    } // while (tryAgain)

    return NO_ERROR;
}

这个方法中,会从mCore->mFreeBuffersmCore->mFreeSlots中获取空闲BufferSlot索引,并且优先从mCore->mFreeBuffers列表中获取:

  • 如果mCore->mFreeBuffers列表不为空,则从该列表中获取第一个Slot索引返回;
  • 如果mCore->mFreeBuffers列表已经为空,则从mCore->mFreeSlots数组中获取一个未绑定GraphicBuffer的slot,并在dequeueBuffer后续操作执行分配Buffer操作后绑定GB到对应BufferSlot上。

从以上两列表获取slot方法如下:

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

// 返回mCore->mFreeBuffers的头元素
int BufferQueueProducer::getFreeBufferLocked() const {
    if (mCore->mFreeBuffers.empty()) {
        return BufferQueueCore::INVALID_BUFFER_SLOT;
    }
    int slot = mCore->mFreeBuffers.front();
    mCore->mFreeBuffers.pop_front();
    return slot;
}

// 返回mCore->mFreeSlots的头元素
int BufferQueueProducer::getFreeSlotLocked() const {
    if (mCore->mFreeSlots.empty()) {
        return BufferQueueCore::INVALID_BUFFER_SLOT;
    }
    int slot = *(mCore->mFreeSlots.begin());
    mCore->mFreeSlots.erase(slot);
    return slot;
}

接下来便根据BBQ模式(默认同步模式)、mDequeueTimeout值和获取结果决定是否要进入等待状态。如果当前已经超过最大Buffer个数或没有空闲BufferSlot时:

  • 同步模式下:通过mCore->mDequeueCondition进入等待状态,直到有空闲BufferSlot或到mDequeueTimeout超时时间后唤醒;
  • 异步模式下:直接返回WOULD_BLOCK

2.2、reallocateBuffer操作

dequeueBuffer中会根据获得的BufferSlot和Surface中传递Buffer参数决定是否进行重新分配Buffer的操作。以下两种情形下会重新分配Buffer:

  • 获得的BufferSlot未绑定GraphicBuffer时;
  • 获得的BufferSlot已绑定GraphicBuffer,但请求Buffer参数发生变化时。

这两种场景时,会将BufferSlot参数重置,并重新初始化一个GraphicBuffer对象绑定到BufferSlot上。

此外,会在dequeueBuffer()返回值上添加BUFFER_NEEDS_REALLOCATION标记,通知Surface执行主动执行requestBuffer()操作。

2.3、BufferSlot状态更新

成功获得空闲BufferSlot后,索引值会从两个空闲列表中移除,并添加到mCore->mActiveBuffers中,同时BufferSlot状态将由FREE变为DEQUEUED

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

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 (mCore->mSharedBufferSlot != found) {
        mCore->mActiveBuffers.insert(found);
    }
    *outSlot = found;
    mSlots[found].mBufferState.dequeue();
    ......   
}

mCore->mActiveBuffers列表用于保存所有处于非空闲状态的BufferSlot对象在mCore->mSlots数组中的索引值,包括处于DEQUEUEDQUEUEDACQUIRED状态的BufferSlot索引。

dequeueBuffer操作过程中BBQ变化过程如下所示:

三、requesetBuffer操作

dequeueBuffer向图形内容生产方返回的是Buffer队列中空闲Buffer的索引,并非Buffer对象本身,这样做传递成本太大,因此在BBQ中每初始化一个GraphicBuffer对象,都必须映射给对应的图形内容生产方。这个映射操作通过requesetBuffer进行。

在Surface中也定义了一个同BBQ大小的mSlots数组,当dequeueBuffer第一次返回一个索引时,Surface::mSlots中对应BufferSlot未绑定GraphicBuffer,这种情况下会调用requestBuffer操作,将mCore->mSlots[slot].mGraphicBuffer赋值给Surface::mSlots[slot].mGraphicBuffer,完成映射。

BufferQueueProducer::requestBuffer()方法如下:

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {

    std::lock_guard<std::mutex> lock(mCore->mMutex);

    .......
    
    mSlots[slot].mRequestBufferCalled = true;
    // 将mSlots[slot].mGraphicBuffer赋值给参数buf
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

还有一种情况是:如果BBQ中由于Buffer参数发生变化,重新分配Buffer后,通过返回值携带BUFFER_NEEDS_REALLOCATION标记告知图形内容生产方重新映射GraphicBuffer,在Surface::dequeueBuffer()中:

cpp 复制代码
// frameworks/native/libs/gui/Surface.cpp

int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {

    ......
    // 执行dequeueBuffer操作
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
                                                            dqInput.height, dqInput.format,
                                                            dqInput.usage, &mBufferAge,
                                                            dqInput.getTimestamps ?
                                                                    &frameTimestamps : nullptr);
    ......
    // 根据返回索引获取GraphicBuffer对象
    sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
    ......
    // 如果GraphicBuffer为空或者返回值携带BUFFER_NEEDS_REALLOCATION标记时,
    // 主动进行requestBuffer操作
    if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
    }
    ......

    return OK;
}

四、queueBuffer操作

当图形内容生产方向Buffer中完成数据填充后,通过queueBuffer操作将Buffer所有权重新返回给BBQ队列。

cpp 复制代码
// frameworks/native/include/gui/IGraphicBufferProducer.h

virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
        QueueBufferOutput* output) = 0;

参数

  • slot:对应BufferSlot在mSlots数组中的索引值;
  • QueueBufferInput& input:客户端中给GraphicBuffer填充的所有属性,如scalingMode、dataspace、transform等;
  • QueueBufferOutput* output:执行完成queueBuffer后向客户端返回的信息参数;

返回值

queueBuffer操作执行成功时,返回NO_ERROR

执行失败时,返回一个负数:

  • NO_INIT:表示该BufferQueue已经被废弃,Producer已经断开连接;
  • BAD_VALUE:参数错误时返回,如fence为空、scaling mode值异常、slot索引越界等。

BufferQueueProducer::queueBuffer()方法如下:

cpp 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    
    ......
    // 反序列化QueueBufferInput获取客户端写入的参数
    input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
            &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
            &getFrameTimestamps);
    const Region& surfaceDamage = input.getSurfaceDamage();
    const HdrMetadata& hdrMetadata = input.getHdrMetadata();
    
    // fence、scaling mode等属性校验
    ......

    // 初始化BufferItem,BufferItem封装了所有来自客户端输入的图形数据
    BufferItem item;
    { // Autolock scope
        std::lock_guard<std::mutex> lock(mCore->mMutex);
        // 异常情况校验和处理,略
        ......
        
        // 设置acquireFence
        mSlots[slot].mFence = acquireFence;
        // 将对应Buffer状态由DEQUEUED状态转变为QUEUED状态
        mSlots[slot].mBufferState.queue();

        // 帧数统计+1
        ++mCore->mFrameCounter;
        // 当前帧序列号
        currentFrameNumber = mCore->mFrameCounter;
        mSlots[slot].mFrameNumber = currentFrameNumber;
        // 开始填充BufferItem
        // 该BufferSlot是否已经执行过acquireBuffer的操作
        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
        // 获取BufferSlot中的GraphicBuffer
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        // GraphicBuffer的裁剪区域
        item.mCrop = crop;
        // GraphicBuffer区域的转换标记,如横纵向翻转、旋转等
        item.mTransform = transform &
                ~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
        // GraphicBuffer区域的转换方式标记,表示是否需要屏幕逆矩阵进行转置
        item.mTransformToDisplayInverse =
                (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
        // GraphicBuffer的缩放模式
        item.mScalingMode = static_cast<uint32_t>(scalingMode);
        // 客户端进行queueBuffer的当前时间戳,单调递增
        item.mTimestamp = requestedPresentTimestamp;
        // 是否自动生成时间戳
        item.mIsAutoTimestamp = isAutoTimestamp;
        // GraphicBuffer的dataspace
        item.mDataSpace = dataSpace;
        // BufferSlot对应的HDR元数据
        item.mHdrMetadata = hdrMetadata;
        // BufferSlot进行queueBuffer的次数
        item.mFrameNumber = currentFrameNumber;
        // BufferSlot索引
        item.mSlot = slot;
        // GraphicBuffer对应的acquireFence
        item.mFence = acquireFence;
        // acquireFence Signal时间
        item.mFenceTime = acquireFenceTime;
        // 该Buffer是否可丢弃,异步模式时会设置为true
        item.mIsDroppable = mCore->mAsyncMode ||
                (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
                (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
                (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
        // 相比上一帧的赃区域
        item.mSurfaceDamage = surfaceDamage;
        // 表示BufferSlot已执行queueBuffer操作
        item.mQueuedBuffer = true;
        // 表示Consumer是否可以在不用等待Buffer可用时就可以获取下一帧,仅用于Share buffer mode
        item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
        // BBQ连接的Producer API
        item.mApi = mCore->mConnectedApi;
        mStickyTransform = stickyTransform;

        // 共享模式时,缓存相关数据
        if (mCore->mSharedBufferMode) {
            mCore->mSharedBufferCache.crop = crop;
            mCore->mSharedBufferCache.transform = transform;
            mCore->mSharedBufferCache.scalingMode = static_cast<uint32_t>(
                    scalingMode);
            mCore->mSharedBufferCache.dataspace = dataSpace;
        }

        output->bufferReplaced = false;
        if (mCore->mQueue.empty()) {
            // 当队列为空时,直接将BufferItem放入队列即可
            mCore->mQueue.push_back(item);
            // 初始化frameAvailableListener接口
            frameAvailableListener = mCore->mConsumerListener;
        } else {
            // 队列不为空时,需要判断队列中的最后一个BufferItem是否可丢弃来选择替换旧Item还是插入
            const BufferItem& last = mCore->mQueue.itemAt(
                    mCore->mQueue.size() - 1);
            // last BufferItem是否是可丢弃的
            if (last.mIsDroppable) {
                // last BufferItem是否已过时,如果还没有过时,这里会进行强制FREE操作,仅针对非共享的Buffer
                if (!last.mIsStale) {
                    // 将对应BufferSlot退出QUEUED状态
                    mSlots[last.mSlot].mBufferState.freeQueued();
                    // 非共享模式时,重置给BufferSlot.mShared属性
                    if (!mCore->mSharedBufferMode &&
                            mSlots[last.mSlot].mBufferState.isFree()) {
                        mSlots[last.mSlot].mBufferState.mShared = false;
                    }
                    // 从mCore->mActiveBuffers中移除,添加到mCore->mFreeBuffers中,
                    // Buffer状态变为空闲(FREE)
                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(last.mSlot);
                        mCore->mFreeBuffers.push_back(last.mSlot);
                        // 表示BufferSlot发生了替换
                        output->bufferReplaced = true;
                    }
                }

                ......
                // 将队列中最后一个BufferItem替换
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;
            } else {
                // 说明last bufferItem是不可丢弃的,这时直接插入新的BufferItem
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;
            }
        }
        // 表示BufferItem已经完成了入对
        mCore->mBufferHasBeenQueued = true;
        // 唤醒dequeueBuffer流程
        mCore->mDequeueCondition.notify_all();
        mCore->mLastQueuedSlot = slot;

        // 填充给客户端同步的QueueBufferOutput对象
        output->width = mCore->mDefaultWidth;
        output->height = mCore->mDefaultHeight;
        output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
        output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
        output->nextFrameNumber = mCore->mFrameCounter + 1;
        ......
    } // Autolock scope

    // 
    if (!mConsumerIsSurfaceFlinger) {
        item.mGraphicBuffer.clear();
    }

    ......
    { // scope for the lock
        // mCallbackCondition是为了保证onFrame*方法在下一次queueBuffer到来之前可以可以有序执行,
        std::unique_lock<std::mutex> lock(mCallbackMutex);
        while (callbackTicket != mCurrentCallbackTicket) {
            mCallbackCondition.wait(lock);
        }
        // 执行回调,通知Consumer
        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);
        } else if (frameReplacedListener != nullptr) {
            frameReplacedListener->onFrameReplaced(item);
        }

        connectedApi = mCore->mConnectedApi;
        lastQueuedFence = std::move(mLastQueueBufferFence);
        // 更新mLastQueueBufferFence
        mLastQueueBufferFence = std::move(acquireFence);

        ++mCurrentCallbackTicket;
        mCallbackCondition.notify_all();
    }
    
    // 等待上一帧Buffer对应acquireFence是否Signal
    if (connectedApi == NATIVE_WINDOW_API_EGL) {
        lastQueuedFence->waitForever("Throttling EGL Production");
    }

    return NO_ERROR;
}

在这个方法中,会根据Slot索引,找到对应BufferSlot的GraphicBuffer,并将其他填充的参数封装到BufferItem中,然后将BufferItem存储到mCore->mQueue中。

4.1、BufferSlot状态更新

对QueueBufferInput参数进行反序列化获取来自客户端传入的数据后, 首先是对mSlots[slot]中对应的BufferSlot对象进行状态更新:

ini 复制代码
// frameworks/native/libs/gui/BufferQueueProducer.cpp

// 更新acquireFence,用于检测GPU是否完成绘制
mSlots[slot].mFence = acquireFence;
// BufferSlot状态由DEQUEUED变为QUEUED
mSlots[slot].mBufferState.queue();
// 更新帧序列号
mSlots[slot].mFrameNumber = currentFrameNumber;

其中,在执行完成mSlots[slot].mBufferState.queue()后,BufferSlot状态将由DEQUEUED状态转换为QUEUED状态。

4.2、填充BufferItem对象

BufferItem封装了所有来自客户端输入的图形数据,传递给SurfaceFlinger的GraphicBuffer属性基本也全都是来自于BufferItem中。

queueBuffer操作中,会完成BufferItem的填充,并在填充完毕后,将BufferItem放入mCore->mQueue列表,进行入队操作。

在BufferItem入队时,如果是异步模式或Share Buffer模式,可能会对mCore->mQueue队列中最后一个BufferItem元素进行替换,其他情况时直接将BufferItem放入队尾。

4.3、执行frameAvailableListener接口回调

在该步操作的最后部分,将mCore->mConsumerListener赋给了frameAvailableListener,并执行frameAvailableListener->onFrameAvailable(item)方法,BLASTBufferQueue类实现了mCore->mConsumerListener接口,通过这个方法将开始BBQ中的消费者组件进行消费的操作。

还有一点要说明的是,在queueBuffer最后,会检查lastQueuedFence是否已经Signal,如果还未Signal,则会进入等待状态阻塞当前进程。lastQueuedFence是上一帧Buffer对应的acquireFence对象,所以这步操作,主要目的是限制QUEUED状态Buffer的个数最多不能超过2个,当某一帧Buffer的上一帧还没有绘制完成时,其下一帧将无法进入QUEUED状态。

queueBuffer操作过程中BBQ变化过程如下所示:

相关推荐
还是奇怪7 小时前
Linux - 安全排查 3
android·linux·安全
Android采码蜂8 小时前
BLASTBufferQueue03-BufferQueueConsumer核心操作
android
2501_915921439 小时前
没有Mac如何完成iOS 上架:iOS App 上架App Store流程
android·ios·小程序·https·uni-app·iphone·webview
码农明明9 小时前
Google Play 应用上架二三事
android·google
红橙Darren12 小时前
手写操作系统 - 环境搭建
android·微信·操作系统
_一条咸鱼_12 小时前
Android Runtime直接内存管理原理深度剖析(73)
android·面试·android jetpack
你听得到1112 小时前
揭秘Flutter图片编辑器核心技术:从状态驱动架构到高保真图像处理
android·前端·flutter
wilinz12 小时前
Flutter Android 端接入百度地图踩坑记录
android·flutter
小袁拒绝摆烂15 小时前
SQL开窗函数
android·sql·性能优化