Surface系统-5-BLASTBufferQueue工作流程概览

853 阅读14分钟

忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。

                        -- 服装学院的IT男

Surface系统-1-应用与SurfaceFlinger建立链接

Surface系统-2-SurfaceControl的创建(java层)

Surface系统-3-SurfaceControl的创建(native层)

Surface系统-4-BLASTBufferQueue和Surface的创建

Surface系统-5-BLASTBufferQueue工作流程概览

本篇同时已收录于Activity短暂的一生系列

正文

Surface完整关系图.png

Surface 系统工作模式就是个生产消费者模型,其中涉及到的几个类之前也大概介绍过。

本来对4个关键方法介绍一下,对整个模型有个概念

下面的代码以软绘的流程来分析

1 软绘流程简述

#ViewRootImpl.java

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
                                boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
        //持有的画布
        final Canvas canvas;
        ...
        try {
            ...
            //1. 申请画布对象,该画布初始大小为dirty的尺寸
            canvas = mSurface.lockCanvas(dirty);
            //设置密度
            canvas.setDensity(mDensity);
        } catch (Surface.OutOfResourcesException e) {
            ...
        } catch (IllegalArgumentException e) {
            ...
            return false;
        } finally {
            ...
        }

        try {
            ......
            // 2. DecorView draw()方法
            mView.draw(canvas);
            ......
        } finally {
            try {
                // 3. 提交绘制的内容到Surface
                surface.unlockCanvasAndPost(canvas);
            } catch (IllegalArgumentException e) {
                ...
            }
        }
        return true;
    }

软绘分3步: 1. canvas = mSurface.lockCanvas(dirty); 申请画布,获取canvas对象 2. mView.draw(canvas); View 树绘制 3. surface.unlockCanvasAndPost(canvas); 绘制完提交

其中第一步对应 BufferQueueProducer::dequeueBuffer 流程,等应用绘制完之后需要显示了,第三步对应 BufferQueueProducer::queueBuffer 将 buff 放回集合,等待消费者拿去给 SurfaceFlinger 合成。

2. dequeueBuffer 获取 buff

2.1 Surface::lockCanvas 获取一个 canvas

这一步从 Surface::lockCanvas 开始分析

# Surface
    // 内部的 canvas
    private final Canvas mCanvas = new CompatibleCanvas();
    // native层的句柄
    long mNativeObject; // package scope only for SurfaceControl access

    public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            ......
            // 打印
            Log.d(TAG, "lockCanvas");
            ......
            // 进入native层
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }

这里就直接进入native方法了,注意下参数。

# android_view_Surface.cpp

static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {

    // 拿到 native 层 Surface
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    ......
    // 定义一个buff,这个很重要,因为View绘制的内容都保存在这个buff上
    ANativeWindow_Buffer buffer;
    // 1. 获取surface的缓冲区
    status_t err = surface->lock(&buffer, dirtyRectPtr);
    ......// 如果获取失败则抛出异常
    // 2. 创建一个Graphics Canvas对象, 参数为对应的java层canvas
    graphics::Canvas canvas(env, canvasObj);
    // 3. 将 buffer设置到canvas中
    canvas.setBuffer(&buffer, static_cast<int32_t>(surface->getBuffersDataSpace()));
    ......
    // 4. 创建一个名为 lockedSurface 的智能指针对象,并将其指向 surface
    sp<Surface> lockedSurface(surface);
    lockedSurface->incStrong(&sRefBaseOwner);
    // 返回一个对Surface对象的引用
    return (jlong) lockedSurface.get();
}
    1. 通过 Surface::lock 获取一块存放绘制数据段buff
    1. 创建一个canvas,这个canvas对应着参数传进来的java层的canvas (这里的canvas实际是SkiaCanvas)
    1. 将 buff 设置给 canvas
    1. 返canva给 java 层进行绘制,后续的绘制内容都将保存在 canvas 下的 buff 中

这么一来,java 层的 Canvas 就有一个从 Surface 中获取的 buff ,这个 buff 可以存放绘制数据。后续 View::Draw 流程会用这个 Canvas 进行绘制,绘制的数据都将保存到这块 buff 中。

这里的 buff 是 ANativeWindow_Buffer 类型,上篇看到的是GraphicBuffer。它们之间有什么关系,也是后面需要关注的一个点。

当前顺着主线继续看 Surface::lock 的实现。

# Surface.h
    // must be used from the lock/unlock thread
    // 重点* 1. 当前正在使用的buff
    sp<GraphicBuffer>           mLockedBuffer;
    //上一次提交的buff
    sp<GraphicBuffer>           mPostedBuffer;

# Surface.cpp
status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{   
    / 重点* 2. 如果当前有已经在使用的buff
    if (mLockedBuffer != nullptr) {
        ALOGE("Surface::lock failed, already locked");
        return INVALID_OPERATION;
    }

    ......
    // 定义一个buff
    ANativeWindowBuffer* out;
    int fenceFd = -1;
    // 重点* 3. 取出一个buff
    status_t err = dequeueBuffer(&out, &fenceFd);
    ALOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
    if (err == NO_ERROR) {
        // 转换成当前要处理的GraphicBuffer
        sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out));
        ......// 重点* 4. 省略从判断是否能从上一帧buff copy 数据逻辑
        // 重点* 5. 锁定buff
        void* vaddr;
        status_t res = backBuffer->lockAsync(
                GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
                newDirtyRegion.bounds(), &vaddr, fenceFd);
        ......
        if (res != 0) {
            err = INVALID_OPERATION;
        } else {
            // 将获取的buff设置给mLockedBuffer
            mLockedBuffer = backBuffer;
            
            // 重点* 6. 将获取的buff的属性设置给出参
            outBuffer->width  = backBuffer->width;
            outBuffer->height = backBuffer->height;
            outBuffer->stride = backBuffer->stride;
            outBuffer->format = backBuffer->format;
            // vaddr赋值给bits
            outBuffer->bits   = vaddr;
        }
    }
    return err;
}
    1. Surface下定义了一个 GraphicBuffer 变量:mLockedBuffer,表示当前真正锁定(使用)的是哪个 buff ,这个值在 unlockCanvasAndPost 会置为 null
    1. 在 Surface::lock 方法首先会判断当前 mLockedBuffer 是否有值,有的话就不需要执行后续逻辑了
    1. 通过 Surface::dequeueBuffer 取出一块 buff ,内部还是 GBP 来实现
    1. 不是每次刷新都会全部重绘整个区域,所以中间省略的代码是判断是否可以从上一帧 copy 一些可以复用的数据。这部分代码不是这次重点,所以省略
    1. GraphicBuffer 一般简称为 buff ,首先他本来只是一个数据结构,但是他为什么这么重要,能作为图像数据的载体,本质原因还是因为其内部的bits变量代表着一块内存区域。这也是为什么被简称为 buff 的原因,android 是基于 linux 的,在 linux 中 buff 代表一块和内存缓存相关的内容。这一步是锁定一块内存区域,在下一步会设置给 GraphicBuffer
    1. 将 backBuffer 的数据设置给出参,也就是最终被设置给 canvas 的那个 buff ,这里要着重注意 bit s的赋值 至于GraphicBuffer的这块区域是怎么设置给canvas的,是**通过 SkBitmap::setPixels 方法将这块内存地址设置给 canvas 下的 bitmap **

其中 GraphicBuffer::lockAsync 就是锁定一块内存区域的方法没什么好看的,主要还是看 Surface::dequeueBuffer 是如何获取一块 buff 的。

2.2 dequeueBuffer

# Surface.cpp

    int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
        // 代表的是mSlots的角标
        int buf = -1;
        ......
        // 获取buff
        // Surface 下的 mGraphicBufferProducer 变量在构造的时候就赋值了,这个代码之前看过了。
        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);                                                   
        ......
        // 申请空间,映射内存区(可能也不是)
        ALOGE("Surface::requestBuffer"); // 自己加的日志
        result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
        ......
        // 出参赋值
        *buffer = gbuf.get();
        ......
    }

知道这个方法的目标就是给参数 buffer 赋值,将定义在 BLASTBufferCore 下的 mSlots 数组下某一块 GraphicBuffer 赋值给它。

    1. 定义一个 int 值,代表后续返回的 buff 所在集合的位置
    1. 通过 GraphicBufferProducer::dequeueBuffer 获取一个可用的 buff
    1. 定义变量 gbuf,其值为 GraphicBufferProducer::dequeueBuffe 方法执行后 mSlots 对应位置的 GraphicBuffer
    1. IGraphicBufferProducer::requestBuffer 这个方法目前没太看明白,后面单独讲
    1. 将 buff 设置给出参参数,也是这个方法最终的目的:获取一个可用的 buff

那么这个方法关注的重点就是 BufferQueueProducer::dequeueBuffer 方法是怎么获取一个 buff 的。

2.3 BufferQueueProducer::dequeueBuffer (重点)

这个方法也是最终的重点,是要分析的4个方法之一。

# 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) {
        ......
        // 定义局部变量,表示mSlots的哪个位置下的buff可以使用
        int found = BufferItem::INVALID_BUFFER_SLOT;
        // 找到空闲的Slot并锁定
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
            if (status != NO_ERROR) {
                return status;
            }
            ......
        }
        ......
        // 设置给出参
        *outSlot = found;
        ......
        // 设置这个位置的buff状态为DEQUEUE
        mSlots[found].mBufferState.dequeue();
        ......
        // 判断是否需要分配 需要则分配
        if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
            //  打印log
            BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
            // 新建一个GraphicBuffer
            sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                    width, height, format, BQ_LAYER_COUNT, usage,
                    {mConsumerName.string(), mConsumerName.size()});
                ......
                // 放入对应的BufferSLot中
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
                ......
        }
        ......
    }

BufferQueueProducer::dequeueBuffer 方法主要是给参数 outSlot 赋值,确认可以使用 mSlots 下的哪个 GraphicBuffer 。

    1. 定义局部变量 found,然后开始执行 waitForFreeSlotThenRelock 方法来查找哪个卡槽的可以使用,如果找到了就将位置设置给 found
    1. 将 found 设置给出参
    1. 设置这个位置的 buff 状态为 DEQUEUE
    1. 如果需要重新分配则新建一个 GraphicBuffer 放到 mSlots 对应的位置下

总之这个方法执行完后,可以知道 mSlots 下哪个位置 的GraphicBuffer 是可用的。

2.4 小结

BufferQueueProducer::dequeueBuffer 逻辑是生成消费者模型的第一步,对应 APP 开发来说就是一个 View 树绘制前,获取 canvas 的步骤。

这里有以下几个注意点:

    1. 方法主要功能就是获取一个可以用的 buff 给生产者绘制 UI
    1. 获取的来源是通过 GraphicBufferProducer::dequeueBuffer,从 mSlots 中获取
    1. 将 mSlots 对应位置的 buff 状态设置为 DEQUEUE
    1. mSlots 之前分析过,实际上定义在 BLASTBufferCore下的, 因为GraphicBufferProducer是其友元类,并且构造的时候也有将GraphicBufferProducer传递进去,所以能直接访问
    1. GraphicBufferProducer 作为 buff 的生产者,面向的是上层应用端,另外他操作的对象本质上是 BLASTBufferCore 下的 mSlots

这部分的调用栈如下:

ViewRootImpl::drawSoftware
    Surface::lockCanvas
        android_view_Surface::nativeLockCanvas  -- JNI
            Surface::lock       -- 获取一个有buff的canvas
                Surface::dequeueBuffer
                    BufferQueueProducer::dequeueBuffer -- dequeueBuffer
                        BufferState::dequeue  --  设置状态 DEQUEUE

其中还有很多细节没分析,比如是怎么从 BLASTBufferCore 获取 buff 的,这个也是一个核心逻辑。

3. queueBuffer

上传通过 BufferQueueProducer::dequeueBuffer 获取到 buff 后会进行绘制,绘制完需要提交到屏幕上,所以绘制完就需要执行 BufferQueueProducer::queueBuffer 来通知整个生成消费模式有个 buff 已经绘制好了。

还是和之前一样已软绘为例,这一步的触发逻辑是 Surface::unlockCanvasAndPost

# Surface

    public void unlockCanvasAndPost(Canvas canvas) {
        synchronized (mLock) {
            checkNotReleasedLocked();

            if (mHwuiContext != null) {
                mHwuiContext.unlockAndPost(canvas);
            } else {
                // 软绘正常走这
                unlockSwCanvasAndPost(canvas);
            }
        }
    }
    private void unlockSwCanvasAndPost(Canvas canvas) {
        ......
        try {
            // 执行native方法
            nativeUnlockCanvasAndPost(mLockedObject, canvas);
        } finally {
            nativeRelease(mLockedObject);
            mLockedObject = 0;
        }
    }

JNI 的实现在 android_view_Surface.cpp

# android_view_Surface.cpp

static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj) {
    // 拿到 native 层 Surface
    sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
    if (!isSurfaceValid(surface)) {
        return;
    }

    // detach the canvas from the surface
    graphics::Canvas canvas(env, canvasObj);
    // 将 canvas 下的 buff 置空 (将Canvas与Surface分离)
    canvas.setBuffer(nullptr, ADATASPACE_UNKNOWN);

    // unlock surface
    // 重点* 解锁 Surface 并将绘图缓冲区提交到 Surface
    status_t err = surface->unlockAndPost();
    if (err < 0) {
        jniThrowException(env, IllegalArgumentException, NULL);
    }
}

重点看 Surface::unlockAndPost 方法,

# Surface.cpp
    status_t Surface::unlockAndPost()
    {
        // mLockedBuffer不能为null
        if (mLockedBuffer == nullptr) {
            ALOGE("Surface::unlockAndPost failed, no locked buffer");
            return INVALID_OPERATION;
        }

        int fd = -1;
        // 解锁内存
        status_t err = mLockedBuffer->unlockAsync(&fd);
        ALOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
        // 重点 * 入队
        err = queueBuffer(mLockedBuffer.get(), fd);
        ALOGE_IF(err, "queueBuffer (handle=%p) failed (%s)",
                mLockedBuffer->handle, strerror(-err));
        // 赋值
        mPostedBuffer = mLockedBuffer;
        // 置空
        mLockedBuffer = nullptr;
        return err;
    }

可以看得出来这里的操作和 Surface::lock 的对应的。 重点方法 BufferQueueProducer::queueBuffer 来了。

# BufferQueueProducer.cpp

    status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
            ......
            // 重点* 1. 局部变量
            BufferItem item;
            ......
            // 拿到buff (根据下标)
            const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
            ......
            // 重点* 2. 设置状态为QUEUE
            mSlots[slot].mBufferState.queue();
            ......
            // 将buff设置到临时变量item中
            item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
            ......// 其他属性的处理
            // 重点* 3. 入队处理
            if (mCore->mQueue.empty()) {
                // When the queue is empty, we can ignore mDequeueBufferCannotBlock
                // and simply queue this buffer
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;
            } else {
                // When the queue is not empty, we need to look at the last buffer
                // in the queue to see if we need to replace it
                // 翻译:当队列不为空时,我们需要查看最后一个缓冲区,在队列中查看是否需要更换
                const BufferItem& last = mCore->mQueue.itemAt(
                        mCore->mQueue.size() - 1);
                if (last.mIsDroppable) {
                    ......
                    mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                    frameReplacedListener = mCore->mConsumerListener;
                } else {
                    mCore->mQueue.push_back(item);
                    frameAvailableListener = mCore->mConsumerListener;
                }
            }
            ......
            if (frameAvailableListener != nullptr) {
                // 重点* 4. 触发回调,告知Consumer
                frameAvailableListener->onFrameAvailable(item);
            } .......
            ......
        }
           
    1. 定义BufferItem类型的局部变量item, 这个对象后面会被添加到BLASTBufferCore下的mQueue中。 (mQueue这个队列的元素是BufferItem,定义在BufferQueueCore.h下)
    1. 将 mSlots 对应位置的buff状态设置为QUEUE
    1. BufferItem的入队处理,这里分为3种情况。但是目的都是一样
    • 3.1 BufferItem放入mQueue
    • 3.2 frameAvailableListener赋值,用于后续通知Consumer
    1. 通知 Consumer(消费者) buff已经准备好了可以拿去给 SurfaceFlinger 进行合成了

经过应用绘制后的数据,会单独构造出一个 BufferItem 放在 mQueue 下,这个 BufferItem 内部也保存了 buff 信息。

3.1 小结

BufferQueueProducer::queueBuffer 和 BufferQueueProducer::dequeueBuffer 的操作是对立的,也比较好理解。 最后是执行了 “frameAvailableListener->onFrameAvailable(item);” ,当一块 buff 绘制好后,生产者的工作就完成了,后面就要交给消费者来处理,最终的目的就把这个 buff 拿给 SurfaceFlinger 最终交给屏幕显示。

而最终执行这个回调的方法是 BLASTBufferQueue::onFrameAvailable ,这个在 BLASTBufferQueue 构造方法里设置的监听,之前分析过。

这部分的调用栈如下:

ViewRootImpl::drawSoftware
    Surface::lockCanvas
    View::draw
    Surface::unlockCanvasAndPost
        Surface::unlockSwCanvasAndPost
            Surface::nativeUnlockCanvasAndPost  -- 开始进入native层
                android_view_Surface::nativeUnlockCanvasAndPost
                    Surface::unlockAndPost
                        Surface::queueBuffer
                            BufferQueueProducer::queueBuffer
                                BufferState::queue -- 设置状态 QUEUE
                                BLASTBufferQueue::onFrameAvailable -- 通知消费者

结合前面获取 buff 的堆栈,完整调用链如下:

ViewRootImpl::drawSoftware
    Surface::lockCanvas
        android_view_Surface::nativeLockCanvas  -- JNI
            Surface::lock       -- 获取一个有buff的canvas
                Surface::dequeueBuffer
                    BufferQueueProducer::dequeueBuffer -- dequeueBuffer
                        BufferState::dequeue  --  设置状态 DEQUEUE
                    BufferQueueProducer::requestBuffer
                GraphicBuffer::lockAsync -- 锁定一块内存给GraphicBuffer
    View::draw
    Surface::unlockCanvasAndPost
        Surface::unlockSwCanvasAndPost
            Surface::nativeUnlockCanvasAndPost  -- 开始进入native层
                android_view_Surface::nativeUnlockCanvasAndPost
                    Surface::unlockAndPost
                        Surface::queueBuffer
                            BufferQueueProducer::queueBuffer
                                BufferState::queue -- 设置状态 QUEUE
                                BLASTBufferQueue::onFrameAvailable -- 通知消费者

4. acquireBuffer

BufferQueueProducer::queueBuffer 将有数据的 buff 包装成一个 BufferItem 放在 mQueue 后会触发 BLASTBufferItemConsumer::onFrameAvailable 回调来告知监听者有数据的 buff 已经拿到了,各位该干活了。

这个 BLASTBufferItemConsumer 是在 BLASTBufferQueue 创建的时候构建的,BufferQueueProducer::queueBuffer 中通过"mCore->mConsumerListener"来获取的监听者其实就是BLASTBufferItemConsumer,构建的时候调用了 BLASTBufferItemConsumer::setFrameAvailableListener 传递了“this”,也就是设置监听者为 BLASTBufferQueue。 也就是说 BufferQueueProducer::queueBuffer 中触发的 onFrameAvailable 回调执行是在 BLASTBufferQueue::onFrameAvailable 中。

# BLASTBufferQueue.cpp 

    void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
        ......
        // 最终会提交给 SurfaceFlinger 合成
        acquireNextBufferLocked(std::nullopt);
        ......
    }

忽略其他只看当前分析的主线

# BLASTBufferQueue.cpp

    status_t BLASTBufferQueue::acquireNextBufferLocked(
        const std::optional<SurfaceComposerClient::Transaction*> transaction) {
            ......
            // 准备事务对象 Transaction
            SurfaceComposerClient::Transaction localTransaction;
            bool applyTransaction = true;
            SurfaceComposerClient::Transaction* t = &localTransaction;
            if (transaction) {
                t = *transaction;
                applyTransaction = false;
            }
            ......
            // 1. 调用 acquireBuffer 函数 从队列中获取到一个 BufferItem 对象 
            BufferItem bufferItem;
            status_t status =
                    mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
            ......
            // 拿到buff
            auto buffer = bufferItem.mGraphicBuffer;
            // t 是准备提交给 SurfaceFlinger 的事务对象
            // 根据 bufferItem 来配置
            ......
            // 2. SurfaceFliger 完成消费后回调,释放这个buff
            auto releaseBufferCallback =
                    std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
                            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
            ......
            // 3. 注意这里传入了回调对象和buff
            t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, mProducerId,
                        releaseBufferCallback);
            t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
            ......// 忽略一大堆 事务的配置
            mergePendingTransactions(t, bufferItem.mFrameNumber);
            if (applyTransaction) { // 进入分支
                // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
                // 4. 提交事务 apply
                t->setApplyToken(mApplyToken).apply(false, true);
                mAppliedLastTransaction = true;
                mLastAppliedFrameNumber = bufferItem.mFrameNumber;
            } else {
                ......
            }
            ......
            return OK;
        }

这个方法很长,目前简单整理出3步:

    1. 通过 GraphicBufferConsumer::acquireBuffer 获取到buff 这个也是本次分析的重点
    1. 构建了 releaseBuffer 回调
    1. 构建一个事务,并且将 buff 和回调设置了进去
    1. 执行事务,后面逻辑会触发 SurfaceFlinger 相关方法最终申请 Vsync 完成上帧

目前只看 BufferItemConsumer::acquireBuffer 方法,看看是怎么给参数 bufferItem 赋值的

# BufferItemConsumer.cpp

    status_t BufferItemConsumer::acquireBuffer(BufferItem *item,
            nsecs_t presentWhen, bool waitForFence) {
                ......
                // 这个方法定义在父类中
                err = acquireBufferLocked(item, presentWhen);
                ......
                // 拿到对应的 buff 赋值给参数
                item->mGraphicBuffer = mSlots[item->mSlot].mGraphicBuffer;

                return OK;
            }

# ConsumerBase.cpp
    status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
            nsecs_t presentWhen, uint64_t maxFrameNumber) {
            ......
            // 这个 mConsumer 是 BufferQueueConsumer
            status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
            ......
    
        }

这里经过 ConsumerBase::acquireBufferLocked 处理后 “item->mSlot” 已经有值了,再将对应下标的 mGraphicBuffer 赋值给参数流程就结束了。

4.1 BufferQueueConsumer::acquireBuffer

经过上面的调用终于来到了 BufferQueueConsumer::acquireBuffer 方法。

# BufferQueueConsumer.cpp

    status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
            nsecs_t expectedPresent, uint64_t maxFrameNumber) {
            ......
            // 获取mQueue队列的迭代器
            BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
            ......
            // 迭代mQueue元素
            while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
                .......
                mCore->mQueue.erase(front);
                front = mCore->mQueue.begin();
            }
            // 初始化一个无效的缓冲区插槽变量 
            int slot = BufferQueueCore::INVALID_BUFFER_SLOT;
            // 一些检查
            if (sharedBufferAvailable && mCore->mQueue.empty()) {
                ......
            } else if {
                ......  
            } else {
                // 获取当前缓冲区的插槽,并将当前缓冲区的属性复制到输出缓冲区中 
                slot = front->mSlot;
                *outBuffer = *front;
            }
            ......
                // 2个方法都是将状态改为 ACQUIRED
                if (mCore->mQueue.empty()) {
                    mSlots[slot].mBufferState.acquireNotInQueue();
                } else {
                    mSlots[slot].mBufferState.acquire();
                }
            ......
            
            if (outBuffer->mAcquireCalled) {
                // 对应BufferItem下的buff置空
                outBuffer->mGraphicBuffer = nullptr;
            }
            // 将 Buffer 从 mQueue 中移除
            mCore->mQueue.erase(front);
            ......
            return NO_ERROR;
    }
    1. 将 mSlots 对应位置的 BufferState 改为 ACQUIRED 状态
    1. 将该 Buffer 从 mQueue 中移除

4.2 小结

这部分是应用完成会之后,由消费者拿着 buff 去使用的逻辑,目前只简单看了一下 BufferQueueConsumer::acquireBuffer 逻辑,完整的逻辑后面还应该有 SurfaceFlinger 的流程已经 HWC 合成。

这部分调用栈如下:

BufferQueueProducer::queueBuffer --  生产者绘制完成
    BLASTBufferQueue::onFrameAvailable
        BLASTBufferQueue::acquireNextBufferLocked
            BufferItemConsumer::acquireBuffer
                ConsumerBase::acquireBufferLocked
                    BufferQueueConsumer::acquireBuffer
                        BufferState::acquire -- 状态改为 ACQUIRED
                        BLASTBufferQueue::onBufferReleased -- 最终会通知到SurfaceFlinger
        SurfaceComposerClient::Transaction::init  -- 构建提交给 SF 的事务
        SurfaceComposerClient::Transaction::apply -- 提交

5. releaseBuffer

# BLASTBufferQueue.cpp
    static void releaseBufferCallbackThunk(wp<BLASTBufferQueue> context, const ReleaseCallbackId& id,
                                        const sp<Fence>& releaseFence,
                                        std::optional<uint32_t> currentMaxAcquiredBufferCount) {
        sp<BLASTBufferQueue> blastBufferQueue = context.promote();
        if (blastBufferQueue) {
            // 走这
            blastBufferQueue->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount);
        } else {
            ALOGV("releaseBufferCallbackThunk %s blastBufferQueue is dead", id.to_string().c_str());
        }
    }

    void BLASTBufferQueue::releaseBufferCallback(
            const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
            std::optional<uint32_t> currentMaxAcquiredBufferCount) {
        std::lock_guard _lock{mMutex};
        BBQ_TRACE();
        releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount,
                                    false /* fakeRelease */);
    }
    void BLASTBufferQueue::releaseBufferCallbackLocked(
        const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
        std::optional<uint32_t> currentMaxAcquiredBufferCount, bool fakeRelease) {
        ......
        // 主线
        releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
        ......
    }
    
    void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
                                        const sp<Fence>& releaseFence) {
        ......
        ALOGE("biubiubiu C++ BLASTBufferQueue::releaseBuffer");
        mNumAcquired--;
        BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber);
        BQA_LOGV("released %s", callbackId.to_string().c_str());
        // 调用到 BufferQueueConsumer
        mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
        mSubmitted.erase(it);
        // Remove the frame number from mSyncedFrameNumbers since we can get a release callback
        // without getting a transaction committed if the buffer was dropped.
        mSyncedFrameNumbers.erase(callbackId.framenumber);
    }

5.1 BufferQueueConsumer::releaseBuffer

SurfaceFlinger 使用完buff后,需要将buff 进行释放,这一步由 BufferQueueConsumer::releaseBuffer 完成

# BufferQueueConsumer.cpp
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
        const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
        EGLSyncKHR eglFence) {
            ......
            sp<IProducerListener> listener;
            ......
            // 释放,FREE 状态
            mSlots[slot].mBufferState.release();
            if (!mSlots[slot].mBufferState.isShared()) {
                //  从mActiveBuffers中移除
                mCore->mActiveBuffers.erase(slot);
                // 加入到mFreeBuffers中
                mCore->mFreeBuffers.push_back(slot);
            }
            ......
            if (listener != nullptr) {
                // 回调
                listener->onBufferReleased();
            }
            return NO_ERROR;
        }
    1. 将 BufferState 改为 FREE 状态,
    1. 将 BufferlSlot 放在 mFreeBuffers 集合

这部分调用链如下:

BLASTBufferQueue::releaseBufferCallbackThunk
    BLASTBufferQueue::releaseBufferCallback
        BLASTBufferQueue::releaseBufferCallbackLocked
            BLASTBufferQueue::releaseBuffer
                BufferQueueConsumer::releaseBuffer