图像显示框架十一——BufferQueue的工作流程(基于Android 15源码分析)

一.入口

生产者入口

本篇详细介绍下生产者--图形缓冲区--消费者这个模型的具体工作流程,我们还是从应用层调用流程着手。

在demo示例中,我们获取buffer-->填充数据-->送出显示的代码(简略版伪代码,重点分析BufferQueue流程的)如下:

复制代码
        // 9. 从生产者(Surface)出队一个缓冲区,用于后续绘制操作
        //    出队操作会从BufferQueue中取出一个空闲缓冲区,返回缓冲区的ANativeWindowBuffer指针
        //    同时返回一个释放栅栏(releaseFenceFd),用于等待前一个使用者完成对该缓冲区的操作
        ANativeWindowBuffer *nativeBuffer = nullptr;
        int releaseFenceFd = -1;
        err = nativeWindow->dequeueBuffer(nativeWindow, &nativeBuffer, &releaseFenceFd);
        if (err != NO_ERROR) {
            ALOGE("error: dequeueBuffer failed: %s (%d)",
                    strerror(-err), -err);
            break;
        }

        // 10. 通过释放栅栏确保当前代码真正控制了这个出队的缓冲区
        //     释放栅栏表示GPU或之前的消费者对该缓冲区的使用已经完成
        //     等待这个栅栏确保我们可以安全地写入缓冲区,避免数据竞争
        sp<Fence> releaseFence(new Fence(releaseFenceFd));
        int waitResult = releaseFence->waitForever("dequeueBuffer_EmptyNative");
        if (waitResult != OK) {
            ALOGE("dequeueBuffer_EmptyNative: Fence::wait returned an error: %d", waitResult);
            break;
        }

        // 将ANativeWindowBuffer转换为GraphicBuffer对象以便后续操作
        sp<GraphicBuffer> buf(GraphicBuffer::from(nativeBuffer));

        // 11. 调用lock函数获取缓冲区的虚拟内存地址
        //     GRALLOC_USAGE_SW_WRITE_OFTEN标志表示我们将频繁地通过CPU写入此缓冲区
        //     lock成功后,img指针指向缓冲区在内存中的起始位置,我们可以直接向其中写入数据
        uint8_t* img = nullptr;
        err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
        if (err != NO_ERROR) {
            ALOGE("error: lock failed: %s (%d)", strerror(-err), -err);
            break;
        }

        // 12. 使用RGB数据填充缓冲区
        //     countFrame是一个帧计数器,用于在三帧之间循环,每帧显示一种颜色(红、绿、蓝)
        //     fillRGBA8Buffer函数将缓冲区填充为指定的颜色,这里依次为红、绿、蓝
        countFrame = (countFrame+1)%3;
        fillRGBA8Buffer(img, nativeSurface->width(), nativeSurface->height(), buf->getStride(),
                        countFrame == 0 ? 255 : 0,  // 红色分量
                        countFrame == 1 ? 255 : 0,  // 绿色分量
                        countFrame == 2 ? 255 : 0); // 蓝色分量

        // 解锁缓冲区,表示我们已经完成对缓冲区的CPU写入操作
        err = buf->unlock();
        if (err != NO_ERROR) {
            ALOGE("error: unlock failed: %s (%d)", strerror(-err), -err);
            break;
        }

        // 13. 将处理完成的缓冲区重新入队,准备显示
        //     queueBuffer会将缓冲区标记为已完成,可以提交给消费者(如SurfaceFlinger)进行合成显示
        //     同时返回一个获取栅栏(acquireFenceFd),消费者需要使用此栅栏等待GPU渲染完成
        int acquireFenceFd = -1;
        err = nativeWindow->queueBuffer(nativeWindow, buf->getNativeBuffer(), acquireFenceFd);
        if (err != NO_ERROR) {
            ALOGE("error: queueBuffer failed: %s (%d)", strerror(-err), -err);
            break;
        }

如下是对上述流程的总结:

步骤 函数 作用 对应 BufferQueue 状态变化
9 dequeueBuffer 从 BufferQueue 获取一个空闲缓冲区,用于写入新内容 缓冲区状态:FREE → DEQUEUED
10 Fence::waitForever 等待 GPU/前一个使用者完成对该缓冲区的操作,确保可以安全写入 同步操作,不改变状态
11 GraphicBuffer::lock 锁定缓冲区,获取 CPU 可访问的内存地址,准备写入数据 缓冲区锁定,CPU 可访问
12 fillRGBA8Buffer 向缓冲区写入实际的图像/像素数据(这里用纯色填充) 数据填充阶段
12 buf->unlock() 解锁缓冲区,表示CPU写入完成 缓冲区解锁
13 queueBuffer 将处理完成的缓冲区提交回 BufferQueue,准备显示 缓冲区状态:DEQUEUED → QUEUED

然后我们把重点聚焦到图形缓冲区的使用上

获取图形缓冲区调用流程:

ANativeWindow::dequeueBuffer ==> Surface::hook_dequeueBuffer ==> Surface::dequeueBuffer ==> BufferQueueProducer::dequeueBuffer

将处理完成的缓冲区返还的流程:

ANativeWindow::queueBuffer ==> Surface::hook_queueBuffer ==> Surface::queueBuffer ==> BufferQueueProducer::queueBuffer

前面的文章提到在BLASTBufferQueue中创建Surface对象时,初始化其成员变量mGraphicBufferProducer,这个就是指向了一个GraphicBufferProducer对象。

当然,上述是应用层调用,只能看出生产者工作流程,消费者如何工作的呢?

生产者与消费者流程概括

先用一张图概括一下:

上述基本展示了BufferQueue的工作逻辑以及buffer的流转过程以及状态变化

流程详细说明

1. 生产者请求空闲缓冲区

  • 操作 : dequeueBuffer()

  • 状态变化: FREE → DEQUEUED

  • 说明: 生产者从缓冲队列请求一个空闲缓冲区,缓冲队列将缓冲区状态标记为"已出队",表示该缓冲区已被生产者占用

2. 生产者填充缓冲区

  • 操作: FILL BUFFER

  • 状态: DEQUEUED

  • 说明: 生产者获得缓冲区后,向其填充数据(如渲染图形、解码视频帧等)

3. 生产者归还已填充缓冲区

  • 操作 : queueBuffer()

  • 状态变化: DEQUEUED → QUEUED

  • 说明: 生产者完成数据填充后,将缓冲区归还给缓冲队列,并标记为"已入队",表示缓冲区已准备好供消费者使用

4. 消费者请求已填充缓冲区

  • 操作 : acquireBuffer()

  • 状态变化: QUEUED → ACQUIRED

  • 说明: 消费者从缓冲队列请求一个已填充数据的缓冲区,缓冲队列将缓冲区状态标记为"已获取",表示该缓冲区已被消费者占用

5. 消费者使用缓冲区

  • 操作: USE BUFFER

  • 状态: ACQUIRED

  • 说明: 消费者使用缓冲区中的数据(如显示到屏幕、进行图像处理等)

6. 消费者释放缓冲区

  • 操作 : releaseBuffer()

  • 状态变化: ACQUIRED → FREE

  • 说明: 消费者使用完缓冲区后,将其释放回缓冲队列,状态重新变为"空闲",等待下一次生产请求

缓冲区状态机

FREE → DEQUEUED → QUEUED → ACQUIRED → FREE

二.生产者相关逻辑

源码位置:framework/native/libs/gui/BufferQueueProducer.cpp

1.申请图形缓冲区

dequeueBuffer:申请图形缓冲区,是从队列中取出一个可用的graphic buffer,它的作用是在应用程序一端准备绘制图像时,向BufferQueue申请一块可用的GraphicBuffer,有了这个buffer就可以把图像数据写入送去做处理和显示。

如下是dequeueBuffer函数加中文注释的代码:

复制代码
status_t BufferQueueProducer::dequeueBuffer(
    int* outSlot,                           // 输出参数,返回获取到的缓冲区槽位索引
    sp<android::Fence>* outFence,          // 输出参数,返回同步栅栏对象(用于GPU/CPU同步)
    uint32_t width, uint32_t height,       // 请求的缓冲区宽高
    PixelFormat format,                     // 请求的缓冲区像素格式
    uint64_t usage,                        // 请求的缓冲区使用标志(如GPU读写、CPU读写等)
    uint64_t* outBufferAge,                // 输出参数,返回缓冲区的"年龄"(被重用的时间间隔)
    FrameEventHistoryDelta* outTimestamps)  // 输出参数,返回帧事件时间戳信息
{
    ATRACE_CALL();  // 开始性能跟踪标记

    // 第一段加锁范围:检查BufferQueue基本状态
    { 
        std::lock_guard<std::mutex> lock(mCore->mMutex);
        mConsumerName = mCore->mConsumerName;

        // 检查BufferQueue是否已被弃用(如Consumer端已断开连接)
        if (mCore->mIsAbandoned) {
            BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
            return NO_INIT;  // 返回错误码:初始化失败
        }

        // 检查Producer是否已正确连接到BufferQueue
        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
            BQ_LOGE("dequeueBuffer: BufferQueue has no connected producer");
            return NO_INIT;
        }
    } 

    BQ_LOGV("dequeueBuffer: w=%u h=%u format=%#x, usage=%#" PRIx64, 
            width, height, format, usage);

    // 检查请求的宽高参数是否有效(宽高需同时为0或同时非0)
    if ((width && !height) || (!width && height)) {
        BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
        return BAD_VALUE;  // 返回错误码:参数错误
    }

    // 初始化返回标志和EGL同步相关变量
    status_t returnFlags = NO_ERROR;
    EGLDisplay eglDisplay = EGL_NO_DISPLAY;
    EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
    bool attachedByConsumer = false;

    // 用于回调的监听器及相关标志
    sp<IConsumerListener> listener;
    bool callOnFrameDequeued = false;
    uint64_t bufferId = 0;

    // 第二段加锁范围:核心处理逻辑
    { 
        std::unique_lock<std::mutex> lock(mCore->mMutex);

        // 等待分配完成:如果没有空闲缓冲区且当前正在分配中,则等待分配完成
        if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
            mDequeueWaitingForAllocation = true;
            mCore->waitWhileAllocatingLocked(lock);  // 等待条件变量
            mDequeueWaitingForAllocation = false;
            mDequeueWaitingForAllocationCondition.notify_all();
        }

        // 如果未指定格式,使用默认格式
        if (format == 0) {
            format = mCore->mDefaultBufferFormat;
        }

        // 合并Consumer端请求的usage标志位
        usage |= mCore->mConsumerUsageBits;

        // 处理默认尺寸:如果宽高均为0,使用默认宽高
        const bool useDefaultSize = !width && !height;
        if (useDefaultSize) {
            width = mCore->mDefaultWidth;
            height = mCore->mDefaultHeight;
            // 自动旋转处理:如果需要自动旋转90度,交换宽高
            if (mCore->mAutoPrerotation &&
                (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
                std::swap(width, height);
            }
        }

        // 核心步骤1:查找可用的缓冲区槽位
        int found = BufferItem::INVALID_BUFFER_SLOT;
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
            // 等待并获取一个空闲的缓冲区槽位[1,2](@ref)
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
            if (status != NO_ERROR) {
                return status;
            }

            // 不应发生的情况:未找到可用槽位
            if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
                BQ_LOGE("dequeueBuffer: no available buffer slots");
                return -EBUSY;  // 返回系统繁忙错误
            }

            const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);

            // 如果不允许分配新缓冲区,但当前缓冲区需要重新分配,则释放它并重新查找
            if (!mCore->mAllowAllocation) {
                if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
                    if (mCore->mSharedBufferSlot == found) {
                        BQ_LOGE("dequeueBuffer: cannot re-allocate a sharedbuffer");
                        return BAD_VALUE;
                    }
                    // 将槽位放回空闲集合,清除槽位状态,重新查找[5](@ref)
                    mCore->mFreeSlots.insert(found);
                    mCore->clearBufferSlotLocked(found);
                    found = BufferItem::INVALID_BUFFER_SLOT;
                    continue;  // 继续while循环
                }
            }
        }

        // 获取找到的槽位对应的缓冲区
        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);

        // 检查缓冲区是否需要重新分配(缓冲区为空或参数不匹配)
        bool needsReallocation = buffer == nullptr ||
                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage);

        // 共享缓冲区特殊处理:不能重新分配共享缓冲区
        if (mCore->mSharedBufferSlot == found && needsReallocation) {
            BQ_LOGE("dequeueBuffer: cannot re-allocate a shared buffer");
            return BAD_VALUE;
        }

        // 如果不是共享缓冲区模式,将找到的槽位加入活跃缓冲区集合
        if (mCore->mSharedBufferSlot != found) {
            mCore->mActiveBuffers.insert(found);  // 添加到活跃集合[1,5](@ref)
        }
        *outSlot = found;  // 设置输出槽位
        ATRACE_BUFFER_INDEX(found);  // 跟踪缓冲区索引

        // 记录是否需要重新分配(由Consumer触发)
        attachedByConsumer = mSlots[found].mNeedsReallocation;
        mSlots[found].mNeedsReallocation = false;

        // 改变缓冲区状态:从FREE变为DEQUEUED[2,5](@ref)
        mSlots[found].mBufferState.dequeue();

        // 核心步骤2:处理缓冲区分配需求
        if (needsReallocation) {
            // 详细的重分配日志记录
            if (CC_UNLIKELY(ATRACE_ENABLED())) {
                // 跟踪信息,记录重分配详情
            }
            // 重置槽位状态,准备重新分配缓冲区
            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;  // 标记正在分配中

            returnFlags |= BUFFER_NEEDS_REALLOCATION;  // 设置重分配标志
        } else {
            // 计算缓冲区年龄:当前帧号与缓冲区最后使用帧号的差值
            mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
        }

        BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge);

        // 检查fence是否异常
        if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) {
            BQ_LOGE("dequeueBuffer: about to return a NULL fence");
        }

        // 设置输出fence(共享缓冲区模式有特殊处理)
        eglDisplay = mSlots[found].mEglDisplay;
        eglFence = mSlots[found].mEglFence;
        *outFence = (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == found) ?
                Fence::NO_FENCE : mSlots[found].mFence;  // 共享模式可能不需要fence
        mSlots[found].mEglFence = EGL_NO_SYNC_KHR;  // 重置EGL fence
        mSlots[found].mFence = Fence::NO_FENCE;     // 重置fence

        // 共享缓冲区模式初始化:首次dequeue时设置共享槽位
        if (mCore->mSharedBufferMode && 
            mCore->mSharedBufferSlot == BufferQueueCore::INVALID_BUFFER_SLOT) {
            mCore->mSharedBufferSlot = found;
            mSlots[found].mBufferState.mShared = true;  // 标记为共享状态
        }

        // 设置回调标志(如果不是重新分配的情况)
        if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
            callOnFrameDequeued = true;
            bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
        }

        listener = mCore->mConsumerListener;  // 获取Consumer监听器
    } 

    // 核心步骤3:如果需要重新分配,创建新的GraphicBuffer
    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);

        // 创建新的GraphicBuffer对象[2,4](@ref)
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
        // 扩展分配路径(支持额外选项)
        std::vector<GraphicBufferAllocator::AdditionalOptions> tempOptions;
        // ... 准备分配选项
        const GraphicBufferAllocator::AllocationRequest allocRequest = {
                .importBuffer = true,
                .width = width,
                .height = height,
                .format = format,
                .layerCount = BQ_LAYER_COUNT,
                .usage = usage,
                .requestorName = {mConsumerName.c_str(), mConsumerName.size()},
                .extras = std::move(tempOptions),
        };
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
#else
        // 标准分配路径
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
            width, height, format, BQ_LAYER_COUNT, usage,
            {mConsumerName.c_str(), mConsumerName.size()});
#endif

        status_t error = graphicBuffer->initCheck();  // 检查分配是否成功

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

            if (error == NO_ERROR && !mCore->mIsAbandoned) {
                // 设置缓冲区世代号并保存到对应槽位
                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;  // 关联缓冲区到槽位
                callOnFrameDequeued = true;
                bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
            }

            // 清除分配中标志并通知等待线程
            mCore->mIsAllocating = false;
            mCore->mIsAllocatingCondition.notify_all();

            // 错误处理:分配失败时清理状态
            if (error != NO_ERROR) {
                mCore->mFreeSlots.insert(*outSlot);
                mCore->clearBufferSlotLocked(*outSlot);
                BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
                return error;
            }

            // 检查是否在分配过程中被弃用
            if (mCore->mIsAbandoned) {
                mCore->mFreeSlots.insert(*outSlot);
                mCore->clearBufferSlotLocked(*outSlot);
                BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                return NO_INIT;
            }

            VALIDATE_CONSISTENCY();  // 验证内部一致性(调试用)
        } 
    }

    // 核心步骤4:回调通知
    if (listener != nullptr && callOnFrameDequeued) {
        listener->onFrameDequeued(bufferId);  // 通知Consumer帧已出队[2](@ref)
    }

    // 处理Consumer触发的重分配标志
    if (attachedByConsumer) {
        returnFlags |= BUFFER_NEEDS_REALLOCATION;
    }

    // 核心步骤5:EGL fence同步等待
    if (eglFence != EGL_NO_SYNC_KHR) {
        EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, 1000000000);
        // 处理等待结果(错误或超时)
        if (result == EGL_FALSE) {
            BQ_LOGE("dequeueBuffer: error %#x waiting for fence", eglGetError());
        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
            BQ_LOGE("dequeueBuffer: timeout waiting for fence");
        }
        eglDestroySyncKHR(eglDisplay, eglFence);  // 销毁EGL同步对象
    }

    BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
            *outSlot, mSlots[*outSlot].mFrameNumber,
            mSlots[*outSlot].mGraphicBuffer != nullptr ?
            mSlots[*outSlot].mGraphicBuffer->handle : nullptr, returnFlags);

    // 设置输出参数
    if (outBufferAge) {
        *outBufferAge = mCore->mBufferAge;
    }
    addAndGetFrameTimestamps(nullptr, outTimestamps);  // 填充时间戳信息

    return returnFlags;  // 返回最终状态(可能包含BUFFER_NEEDS_REALLOCATION标志)
}

BufferQueueProducer::dequeueBuffer是 Android 图形系统中 生产者-消费者模型​ 的关键接口,主要作用是为生产者(如 App)获取一个可用的图形缓冲区(GraphicBuffer)用于内容绘制。

核心功能流程
  1. 状态验证:检查 BufferQueue 是否有效且已连接。

  2. 参数处理:处理宽高、格式等参数,使用默认值(若需要)并与 Consumer 的 usage 标志合并。

  3. 查找空闲槽位 :通过 waitForFreeSlotThenRelock在 BufferQueueCore 的缓冲区槽位数组 mSlots中寻找可用槽位。优先从 mFreeBuffers(有绑定 GraphicBuffer 且状态为 FREE 的槽位集合)中获取,其次从 mFreeSlots(无绑定 GraphicBuffer 且状态为 FREE 的槽位集合)中获取。

  4. 状态转换 :将获取到的槽位状态从 FREE ​ 转换为 DEQUEUED ,并将其加入 mActiveBuffers集合。

  5. 缓冲区分配/验证 :检查现有 GraphicBuffer 是否满足要求(尺寸、格式等)。如不满足,设置 BUFFER_NEEDS_REALLOCATION标志,并在后续创建新的 GraphicBuffer。

  6. 同步处理 :返回与缓冲区关联的 Fence对象,用于生产者(如 GPU)与后续消费者(如 SurfaceFlinger)之间的同步。

  7. 回调通知 :通过 onFrameDequeued回调通知 Consumer 缓冲区已被取出。

关键数据结构与状态管理
  • BufferQueueCore :核心数据管理器,维护了 mSlots(缓冲区槽位数组)、mFreeBuffersmFreeSlotsmActiveBuffers等关键集合。

  • BufferState :通过 mDequeueCountmQueueCountmAcquireCountmShared标志来精确追踪每个缓冲区的状态(FREE, DEQUEUED, QUEUED, ACQUIRED, SHARED)。

  • GraphicBuffer:存储图像数据的实际载体,其生命周期由 BufferQueue 管理。

查找空闲槽位

上面的代码中有关键的一步,查找空闲槽位,waitForFreeSlotThenRelock函数,它的作用是在mSlot数组中查找FREE状态的slot,如果找到了就返回这个slot中的index

复制代码
// 等待空闲缓冲区槽位并重新加锁的函数
// 这是BufferQueueProducer中获取可用缓冲区槽位的核心函数
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {
    // 根据调用者类型确定函数名称字符串,用于日志记录
    auto callerString = (caller == FreeSlotCaller::Dequeue) ?
            "dequeueBuffer" : "attachBuffer";
    bool tryAgain = true;
    
    // 循环尝试获取空闲槽位
    while (tryAgain) {
        // 1. 检查BufferQueue是否已被废弃
        if (mCore->mIsAbandoned) {
            BQ_LOGE("%s: BufferQueue has been abandoned", callerString);
            return NO_INIT;  // 返回初始化失败错误
        }

        // 2. 统计当前已出队和已获取的缓冲区数量
        int dequeuedCount = 0;   // 被生产者出队但尚未入队的缓冲区数量
        int acquiredCount = 0;   // 被消费者获取但尚未释放的缓冲区数量
        for (int s : mCore->mActiveBuffers) {
            if (mSlots[s].mBufferState.isDequeued()) {
                ++dequeuedCount;  // 统计DEQUEUED状态的缓冲区
            }
            if (mSlots[s].mBufferState.isAcquired()) {
                ++acquiredCount;  // 统计ACQUIRED状态的缓冲区
            }
        }

        // 3. 检查是否超过最大可出队缓冲区数量限制
        // 此检查仅在已有缓冲区入队后执行,防止生产者出队过多缓冲区
        if (mCore->mBufferHasBeenQueued &&
                dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
            // 当超时时间为负数时,才输出错误日志(通常表示没有超时限制)
            if (mDequeueTimeout < 0) {
                BQ_LOGE("%s: attempting to exceed the max dequeued buffer "
                        "count (%d)", callerString,
                        mCore->mMaxDequeuedBufferCount);
            }
            return INVALID_OPERATION;  // 返回无效操作错误
        }

        // 4. 初始化为无效槽位
        *found = BufferQueueCore::INVALID_BUFFER_SLOT;

        // 5. 检查是否有过多缓冲区在队列中等待处理
        // 在快速断开和重新连接的情况下,可能出现槽位为空但队列中有很多缓冲区的情况
        const int maxBufferCount = mCore->getMaxBufferCountLocked();  // 获取最大缓冲区数量
        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 {
            // 6. 查找空闲槽位或缓冲区的逻辑
            // 如果是共享缓冲区模式且存在共享缓冲区槽位,则总是返回共享槽位
            if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
                    BufferQueueCore::INVALID_BUFFER_SLOT) {
                *found = mCore->mSharedBufferSlot;  // 使用共享缓冲区槽位
            } else {
                // 根据调用者类型使用不同的查找策略
                if (caller == FreeSlotCaller::Dequeue) {
                    // 从dequeue调用:优先查找已有缓冲区的空闲槽位
                    int slot = getFreeBufferLocked();  // 查找有缓冲区的空闲槽位
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;  // 使用找到的已有缓冲区的槽位
                    } else if (mCore->mAllowAllocation) {
                        // 如果允许分配新缓冲区,则查找完全空闲的槽位
                        *found = getFreeSlotLocked();  // 查找空闲槽位
                    }
                } else {
                    // 从attach调用:优先查找完全空闲的槽位
                    int slot = getFreeSlotLocked();  // 查找空闲槽位
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;  // 使用找到的空闲槽位
                    } else {
                        // 如果没有空闲槽位,则查找已有缓冲区的空闲槽位
                        *found = getFreeBufferLocked();
                    }
                }
            }
        }

        // 7. 判断是否需要重新尝试(未找到槽位或缓冲区过多)
        tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
                   tooManyBuffers;
        if (tryAgain) {
            // 8. 非阻塞模式处理
            // 如果处于非阻塞模式(由应用程序控制生产者和消费者)且已获取缓冲区数量未超过限制
            if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
                    (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
                return WOULD_BLOCK;  // 返回阻塞错误
            }
            
            // 9. 等待条件满足
            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 {
                // 无限等待直到条件变量被通知
                mCore->mDequeueCondition.wait(lock);
            }
        }
    } // while (tryAgain)

    return NO_ERROR;  // 成功找到可用槽位
}
1. 函数作用

waitForFreeSlotThenRelock函数是 BufferQueueProducer 中获取可用缓冲区槽位的核心函数,负责在缓冲区槽位紧张时协调生产者和消费者,避免缓冲区过度分配和内存耗尽。

2. 缓冲区状态管理

函数内部维护了重要的缓冲区状态跟踪:

复制代码
// 缓冲区状态转换图
FREE → DEQUEUED → QUEUED → ACQUIRED → FREE
  • dequeuedCount: 被生产者取出但尚未提交的缓冲区数量

  • acquiredCount: 被消费者获取但尚未释放的缓冲区数量

3. 主要逻辑流程
步骤1-2: 状态检查和统计
  • 检查 BufferQueue 是否有效

  • 统计当前处于不同状态的缓冲区数量

步骤3: 出队数量限制检查
复制代码
if (mCore->mBufferHasBeenQueued &&
    dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
    return INVALID_OPERATION;
}

防止生产者一次出队过多缓冲区,可能导致:

  1. 内存浪费

  2. 消费者处理不及时

  3. 系统性能下降

步骤4-5: 缓冲区数量控制
复制代码
bool tooManyBuffers = mCore->mQueue.size() > maxBufferCount;

控制队列中的缓冲区数量,防止生产者生产速度过快超过消费者处理能力。

步骤6: 槽位查找策略

根据调用者和模式使用不同查找策略:

调用者 优先级 策略说明
dequeue 1. getFreeBufferLocked 2. getFreeSlotLocked 优先重用已有缓冲区的槽位,减少分配开销
attach 1. getFreeSlotLocked 2. getFreeBufferLocked 优先使用完全空闲的槽位
步骤7-9: 等待机制

当没有可用槽位或缓冲区过多时:

  1. 非阻塞模式 : 立即返回 WOULD_BLOCK

  2. 超时模式 : 等待指定时间后返回 TIMED_OUT

  3. 阻塞模式: 无限等待直到条件满足

2.填充数据后返还给图形缓冲区

客户端/应用通过调用dequeueBuffer获取到一个可用的buffer后,就可以往这个buffer中填充数据了,在我们的demo中这一过程是在fillRGBA8Buffer函数内完成的,具体可见代码。 填充好数据后,就要把这个buffer再返还给BufferQueue,调用的方法是queueBuffer。

复制代码
// 生产者将已填充数据的缓冲区入队的核心函数
// 这是BufferQueueProducer中处理生产者提交已完成渲染的缓冲区的关键函数
status_t BufferQueueProducer::queueBuffer(int slot,                  // 缓冲区槽位索引
        const QueueBufferInput &input,      // 入队输入参数(包含时间戳、裁剪、变换等信息)
        QueueBufferOutput *output) {        // 入队输出参数(包含帧号、尺寸等信息)
    ATRACE_CALL();                          // 开始性能追踪
    ATRACE_BUFFER_INDEX(slot);              // 追踪特定缓冲区的操作

    // 1. 从输入参数中解析各种信息
    int64_t requestedPresentTimestamp;      // 请求显示时间戳
    bool isAutoTimestamp;                   // 是否自动生成时间戳
    android_dataspace dataSpace;            // 数据空间(颜色空间)
    Rect crop(Rect::EMPTY_RECT);            // 裁剪区域
    int scalingMode;                        // 缩放模式
    uint32_t transform;                     // 变换(旋转、镜像等)
    uint32_t stickyTransform;               // 粘性变换(持久化的变换)
    sp<Fence> acquireFence;                 // 获取栅栏(生产者完成渲染的同步对象)
    bool getFrameTimestamps = false;        // 是否获取帧时间戳
    
    // 将input中的各种参数解压到局部变量
    input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
            &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
            &getFrameTimestamps);
    
    // 获取表面损坏区域和HDR元数据
    const Region& surfaceDamage = input.getSurfaceDamage();
    const HdrMetadata& hdrMetadata = input.getHdrMetadata();

    // 2. 验证acquireFence是否有效(生产者必须提供有效的栅栏)
    if (acquireFence == nullptr) {
        BQ_LOGE("queueBuffer: fence is NULL");
        return BAD_VALUE;  // 返回参数错误
    }

    // 3. 创建FenceTime对象用于追踪栅栏时间
    auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);

    // 4. 验证缩放模式是否有效
    switch (scalingMode) {
        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
            break;  // 有效的缩放模式
        default:
            BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
            return BAD_VALUE;  // 无效的缩放模式
    }

    // 5. 各种监听器和状态变量的声明
    sp<IConsumerListener> frameAvailableListener;   // 帧可用监听器
    sp<IConsumerListener> frameReplacedListener;    // 帧替换监听器
    int callbackTicket = 0;                         // 回调票号(用于保证回调顺序)
    uint64_t currentFrameNumber = 0;                // 当前帧号
    BufferItem item;                                // 缓冲区项(将添加到队列)
    int connectedApi;                               // 连接的API类型
    sp<Fence> lastQueuedFence;                      // 上一次入队的栅栏

    // 6. 核心加锁范围(保护BufferQueue核心状态)
    { 
        std::lock_guard<std::mutex> lock(mCore->mMutex);

        // 6.1 检查BufferQueue是否被废弃
        if (mCore->mIsAbandoned) {
            BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
            return NO_INIT;
        }

        // 6.2 检查生产者是否已连接
        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
            BQ_LOGE("queueBuffer: BufferQueue has no connected producer");
            return NO_INIT;
        }

        // 6.3 验证槽位索引有效性
        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
            BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
            return BAD_VALUE;
        } 
        // 6.4 验证缓冲区状态必须是DEQUEUED(否则生产者无权入队)
        else if (!mSlots[slot].mBufferState.isDequeued()) {
            BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
            return BAD_VALUE;
        } 
        // 6.5 验证缓冲区是否已通过requestBuffer分配
        else if (!mSlots[slot].mRequestBufferCalled) {
            BQ_LOGE("queueBuffer: slot %d was queued without requesting "
                    "a buffer", slot);
            return BAD_VALUE;
        }

        // 6.6 共享缓冲区模式特殊处理
        // 如果刚启用共享缓冲区模式,标记第一个入队的缓冲区为共享缓冲区
        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
                BufferQueueCore::INVALID_BUFFER_SLOT) {
            mCore->mSharedBufferSlot = slot;
            mSlots[slot].mBufferState.mShared = true;
        }

        // 6.7 记录详细的入队日志
        BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
                " validHdrMetadataTypes=0x%x crop=[%d,%d,%d,%d] transform=%#x scale=%s",
                slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace,
                hdrMetadata.validTypes, crop.left, crop.top, crop.right, crop.bottom,
                transform,
                BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));

        // 6.8 获取图形缓冲区并验证裁剪区域
        const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
        Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
        Rect croppedRect(Rect::EMPTY_RECT);
        crop.intersect(bufferRect, &croppedRect);
        if (croppedRect != crop) {
            BQ_LOGE("queueBuffer: crop rect is not contained within the "
                    "buffer in slot %d", slot);
            return BAD_VALUE;
        }

        // 6.9 如果数据空间为未知,使用消费者默认值
        if (dataSpace == HAL_DATASPACE_UNKNOWN) {
            dataSpace = mCore->mDefaultBufferDataSpace;
        }

        // 6.10 更新缓冲区状态:保存栅栏,改变状态为QUEUED
        mSlots[slot].mFence = acquireFence;
        mSlots[slot].mBufferState.queue();  // 状态:DEQUEUED → QUEUED

        // 6.11 递增帧计数器并保存当前帧号
        ++mCore->mFrameCounter;
        currentFrameNumber = mCore->mFrameCounter;
        mSlots[slot].mFrameNumber = currentFrameNumber;

        // 6.12 准备BufferItem(将添加到队列的数据结构)
        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        item.mCrop = crop;
        item.mTransform = transform &
                ~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
        item.mTransformToDisplayInverse =
                (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
        item.mScalingMode = static_cast<uint32_t>(scalingMode);
        item.mTimestamp = requestedPresentTimestamp;
        item.mIsAutoTimestamp = isAutoTimestamp;
        item.mDataSpace = dataSpace;
        item.mHdrMetadata = hdrMetadata;
        item.mFrameNumber = currentFrameNumber;
        item.mSlot = slot;
        item.mFence = acquireFence;
        item.mFenceTime = acquireFenceTime;
        
        // 6.13 判断缓冲区是否可丢弃(多种条件)
        item.mIsDroppable = mCore->mAsyncMode ||                // 异步模式
                (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||  // SurfaceFlinger消费者且允许丢弃
                (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||   // 传统缓冲区丢弃
                (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot); // 共享缓冲区模式
        
        item.mSurfaceDamage = surfaceDamage;
        item.mQueuedBuffer = true;
        item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;  // 自动刷新(共享缓冲区)
        item.mApi = mCore->mConnectedApi;

        // 6.14 保存粘性变换
        mStickyTransform = stickyTransform;

        // 6.15 如果是共享缓冲区模式,缓存共享缓冲区数据以便重建BufferItem
        if (mCore->mSharedBufferMode) {
            mCore->mSharedBufferCache.crop = crop;
            mCore->mSharedBufferCache.transform = transform;
            mCore->mSharedBufferCache.scalingMode = static_cast<uint32_t>(scalingMode);
            mCore->mSharedBufferCache.dataspace = dataSpace;
        }

        // 6.16 初始化输出参数
        output->bufferReplaced = false;
        
        // 6.17 队列处理逻辑
        if (mCore->mQueue.empty()) {
            // 队列为空:直接添加缓冲区项
            mCore->mQueue.push_back(item);
            frameAvailableListener = mCore->mConsumerListener;  // 通知消费者有新帧可用
        } else {
            // 队列不为空:检查是否需要替换最后一帧
            const BufferItem& last = mCore->mQueue.itemAt(mCore->mQueue.size() - 1);
            if (last.mIsDroppable) {
                // 最后一帧可丢弃:替换它
                if (!last.mIsStale) {
                    // 释放被替换的缓冲区状态
                    mSlots[last.mSlot].mBufferState.freeQueued();

                    // 共享缓冲区模式特殊处理
                    if (!mCore->mSharedBufferMode &&
                            mSlots[last.mSlot].mBufferState.isFree()) {
                        mSlots[last.mSlot].mBufferState.mShared = false;  // 不再共享
                    }
                    
                    // 如果不是共享缓冲区,将其加入空闲缓冲区列表
                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(last.mSlot);
                        mCore->mFreeBuffers.push_back(last.mSlot);
                        output->bufferReplaced = true;  // 标记缓冲区被替换
                    }
                }

                // 合并损坏区域(将被丢弃帧的损坏区域合并到新帧)
                if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT ||
                    item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) {
                    item.mSurfaceDamage = Region::INVALID_REGION;
                } else {
                    item.mSurfaceDamage |= last.mSurfaceDamage;
                }

                // 覆盖可丢弃的缓冲区
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;  // 通知消费者帧被替换
            } else {
                // 最后一帧不可丢弃:添加到队列末尾
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;  // 通知消费者有新帧可用
            }
        }

        // 6.18 更新BufferQueue状态
        mCore->mBufferHasBeenQueued = true;  // 标记已有缓冲区入队
        mCore->mDequeueCondition.notify_all();  // 唤醒等待出队的生产者
        mCore->mLastQueuedSlot = slot;         // 记录最后入队的槽位

        // 6.19 设置输出参数
        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;

        // 6.20 性能追踪和占用率追踪
        ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
        mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
        
        // 6.21 获取回调票号(保证回调顺序)
        callbackTicket = mNextCallbackTicket++;

        VALIDATE_CONSISTENCY();  // 验证内部一致性

        // 6.22 保存局部状态供锁外使用
        connectedApi = mCore->mConnectedApi;
        lastQueuedFence = std::move(mLastQueueBufferFence);
        mLastQueueBufferFence = std::move(acquireFence);
        mLastQueuedCrop = item.mCrop;
        mLastQueuedTransform = item.mTransform;
    } // 自动锁作用域结束(释放核心锁)

    // 7. 如果不是SurfaceFlinger消费者,清除GraphicBuffer引用
    // SurfaceFlinger在同一进程,不需要通过Binder传递,可以保留引用
    if (!mConsumerIsSurfaceFlinger) {
        item.mGraphicBuffer.clear();
    }

    // 8. 更新帧事件历史
    nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
    NewFrameEventsEntry newFrameEventsEntry = {
        currentFrameNumber,
        postedTime,
        requestedPresentTimestamp,
        std::move(acquireFenceTime)
    };
    addAndGetFrameTimestamps(&newFrameEventsEntry,
            getFrameTimestamps ? &output->frameTimestamps : nullptr);

    // 9. 回调通知(持有回调锁但不持有核心锁)
    {
        std::unique_lock<std::mutex> lock(mCallbackMutex);
        // 9.1 等待轮到自己进行回调(确保回调顺序)
        while (callbackTicket != mCurrentCallbackTicket) {
            mCallbackCondition.wait(lock);
        }

        // 9.2 执行回调
        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);  // 通知新帧可用
        } else if (frameReplacedListener != nullptr) {
            frameReplacedListener->onFrameReplaced(item);    // 通知帧被替换
        }

        // 9.3 递增当前回调票号,唤醒等待的线程
        ++mCurrentCallbackTicket;
        mCallbackCondition.notify_all();
    }

    // 10. EGL API的特殊处理:等待上一个入队的栅栏
    if (connectedApi == NATIVE_WINDOW_API_EGL) {
        // 这里等待允许排队两个完整的缓冲区,但不允许第三个
        // 在帧时间变化的情况下,这种设计在延迟和吞吐量之间做了一个小的权衡,偏向于延迟
        lastQueuedFence->waitForever("Throttling EGL Production");
    }

    return NO_ERROR;  // 成功返回
}

1. 函数作用

BufferQueueProducer::queueBuffer是生产者-消费者模型中生产者提交已完成渲染的缓冲区的关键函数。它将一个已填充数据的缓冲区提交给 BufferQueue,供消费者(如 SurfaceFlinger)使用。

2. 在生产者-消费者流程中的位置

根据用户提供的图片,这个函数对应流程中的 queueBuffer操作:

复制代码
Producer → BufferQueue → Consumer
           ↓
       queueBuffer
           ↓
      QUEUED BUFFER

3. 核心流程

步骤1-4: 参数解析与验证
  • QueueBufferInput解析各种渲染参数

  • 验证 acquireFence必须有效(生产者必须提供同步栅栏)

  • 验证缩放模式等参数的有效性

步骤5-6: 核心加锁处理

这是函数最复杂的部分,包含:

  1. 状态验证:检查 BufferQueue 状态、生产者连接、槽位有效性

  2. 共享缓冲区处理:如果是第一个入队的共享缓冲区,标记它

  3. 参数验证:验证裁剪区域在缓冲区内

  4. 状态更新:缓冲区状态从 DEQUEUED 变为 QUEUED

  5. BufferItem创建:创建队列项,包含所有渲染参数

  6. 队列管理:处理可丢弃缓冲区的替换逻辑

步骤7-10: 锁外处理
  1. 资源清理:如果不是 SurfaceFlinger,清理缓冲区引用

  2. 时间戳更新:记录帧事件时间戳

  3. 回调通知:通知消费者有新帧可用或帧被替换

  4. EGL同步:如果是 EGL API,进行特殊同步处理

到此queueBuffer 就分析完了。其中会调用了mCore->mConsumerListener; 的回调接口,来通知consumer消费数据,那么这个回调接口是在何时被传入的,接口的是什么? 这个疑问留到下一篇单独讲解。

三.总结

本文主要聚焦生产者一端的处理逻辑,分析了如何获取buffer以及填充数据后返还buffer的流程。下一篇来介绍消费者一端的处理逻辑

相关推荐
爬山算法1 小时前
Hibernate(72)如何在NoSQL数据库中使用Hibernate?
java·nosql·hibernate
毕设源码-赖学姐1 小时前
【开题答辩全过程】以 基于spring boot的国学诗词网站设计与实现--为例,包含答辩的问题和答案
java·spring boot·后端
diediedei2 小时前
用Pygame开发你的第一个小游戏
jvm·数据库·python
程序员后来2 小时前
Redis基本数据类型及其应用:从原理到实战的完整指南
数据库·redis·缓存
2301_790300962 小时前
用Python制作一个文字冒险游戏
jvm·数据库·python
naruto_lnq2 小时前
使用Seaborn绘制统计图形:更美更简单
jvm·数据库·python
Hellc0072 小时前
Jenkins 上下游 Job + Docker 镜像部署完整实战(避坑版)
java·docker·jenkins
weixin_395448912 小时前
mult_yolov5_post_copy.h_cursor_0129
linux·网络·人工智能
IP搭子来一个2 小时前
隧道IP代理是什么?原理与应用全解析
网络·网络协议·tcp/ip