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变化过程如下所示:

相关推荐
Kapaseker39 分钟前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴1 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android