BufferQueue 学习总结(内附动态图)

一、前言

《最简单的BufferQueue测试程序(一)》
《最简单的BufferQueue测试程序(二)》
《最简单的BufferQueue测试程序(三)》

本文仅对BufferQueue最基本的操作接口进行讲解,不包含 SurfaceFlinger、Surface 等上层封装的概念介绍。

本文适用对象:Android小白
Android版本:8.1

阅读完本文后,你将了解如下内容:

  • 什么是BufferQueue
  • BufferQueue内部操作的原理是什么
  • BufferQueue跨进程操作是怎么实现的
  • 如何写一个最简单的BufferQueue测试程序

二、基本概念

首先来自Google官方的原图:

图片来源

但本人更喜欢下面这张图,摘自林学森的《深入理解Android内核设计思想》,该图更好的阐述了BufferQueue的基本操作流程:
在这里插入图片描述

buffer的5种状态:FREEDEQUEUEDQUEUEDACQUIREDSHARED

在较新的Android版本中(5.1及以上),buffer的状态由引用计数来表示,如下表(摘自 frameworks/native/include/gui/BufferSlot.h):

statemSharedmDequeueCountmQueueCountmAcquireCount
FREEfalse000
DEQUEUEDfalse100
QUEUEDfalse010
ACQUIREDfalse001
SHAREDtrueanyanyany

三、BufferQueue内部结构

BufferQueue核心代码由如下3部分组成:

  • BufferQueueCore
  • BufferQueueProducer
  • BufferQueueConsumer

BufferQueueCore 负责维护 BufferQueue 的基本数据结构,而 BufferQueueProducer 和 BufferQueueConsumer 则负责提供操作 BufferQueue 的基本接口。

BufferQueueCore在frameworks/native/include/gui/BufferQueueCore.h中定义。
下图为BufferQueueCore的内部数据结构图:
在这里插入图片描述
说明:

成员变量说明
mQueue存放BufferItem的FIFO队列
mSlotsBufferSlot结构体数组,数组长度为64
mFreeSlotsBufferSlot状态为FREE,且没有GraphicBuffer与之相绑定的slot集合
mFreeBuffersBufferSlot状态为FREE,且有GraphicBuffer与之相绑定的slot集合
mActiveBuffersBufferSlot状态不为FREE(即DEQUEUED、QUEUED、ACQUIRED、SHARED)的slot集合。既然状态不是FREE,那么该BufferSlot必然有一个GraphicBuffer与之相绑定
mUnusedSlots未参与使用的slot集合,由 mMaxBufferCount 决定

所以就有了如下等式:

mSlots = mFreeSlots + mFreeBuffers + mActiveBuffers + mUnusedSlots

四、基本函数

《最简单的BufferQueue测试程序(一)》中,演示了一个BufferQueue的基本操作流程。dequeue/queue/acquire/release,这些基本函数内部到底做了什么操作呢?我们一起来研究一下。

dequeueBuffer()

dequeueBuffer() 默认优先从mFreeBuffers中获取slot,因为mFreeBuffers中的slot已经有buffer与之绑定过了,这样就不用再重新分配buffer了。过程如下:
在这里插入图片描述
如果mFreeBuffers为空,则从mFreeSlots中获取slot:
在这里插入图片描述

  1. dequeueBuffer() 优先从mFreeBuffers中获取一个slot,同时将其对应的BufferSlot状态从FREE修改为DEQUEUED,并将该slot从mFreeBuffers迁移到mActiveBuffers中。
  2. 如果mFreeBuffers为空,则从mFreeSlots中获取slot,并为它分配一块指定大小的buffer,同时将其对应的BufferSlot状态从FREE修改为DEQUEUED,然后将该slot从mFreeSlots迁移到mActiveBuffers中。
  3. 将获取到的slot作为出参返回给调用者。如果该slot绑定的buffer是重新分配的,则返回值为BUFFER_NEEDS_REALLOCATION,否则为NO_ERROR。

可以看到,与mFreeBuffers相比,从mFreeSlots中获取slot时,要多一步分配内存的操作。当然,如果mFreeBuffers不为空,但是里面没有我们想要的buffer,比如buffer size不匹配,那么这时候也会通过触发Allocate的动作来重新分配buffer。

requestBuffer()

requestBuffer() 可以说是最没有表现力的一个函数了,因为它本质上不涉及到slot、state以及buffer的任何修改,它仅仅只是从给定的slot中取出与之绑定的GraphicBuffer指针,然后返回,仅此而已。

一般在dequeueBuffer()重新分配内存后(即函数返回值为BUFFER_NEEDS_REALLOCATION),才需要调用requestBuffer()来获取新的GraphicBuffer指针。

queueBuffer()

queueBuffer()的内部操作流程如下(以slot5为例):

在这里插入图片描述

  1. queueBuffer()根据调用者传入的slot参数,将其对应的BufferSlot状态从DEQUEUED修改为QUEUED,并根据该BufferSlot的信息生成一个BufferItem对象,然后添加到mQueue队列中。
  2. 调用Consumer的Listener监听函数,通知Consumer可以被acquireBuffer了。

acquireBuffer()

acquireBuffer()内部操作流程如下:

在这里插入图片描述

acquireBuffer()从mQueue队列中取出1个BufferItem,并作为出参返回给调用者,同时修改该BufferItem对应的slot状态:QUEUED —> ACQUIRED。

releaseBuffer()

releaseBuffer()内部操作流程如下(以slot0为例):
在这里插入图片描述

  1. releaseBuffer()根据调用者传入的slot参数,将其对应的BufferSlot状态从ACQUIRED修改为FREE,并将该slot从mActiveBuffers中迁移到mFreeBuffers中。注意,这里并没有对该slot绑定的buffer进行任何解绑操作。
  2. 调用Producer的Listener监听函数,通知Producer可以dequeueBuffer了。

五、特殊函数

除了上面提到的dequeue/queue/acquire/release这些基本操作函数外,BufferQueue还为我们提供了一些特殊函数接口,方便调用者在一些非常规流程中使用。
这些特殊函数包括attach/detach/cancel等操作,在《最简单的BufferQueue测试程序(二)》中,已经演示了如何使用这些特殊函数。但是这些特殊接口内部到底又做了什么操作呢?我们一起往下看。

producer->attachBuffer()

描述: 给定1个GraphicBuffer指针,优先从mFreeSlots中获取一个slot,并与该GraphicBuffer绑定。slot从mFreeSlots/mFreeBuffers迁移到mActiveBuffers。
状态: FREE —> DEQUEUED

producer->detachBuffer()

描述: 给定1个slot,将该slot绑定的buffer解绑。slot从mActiveBuffers迁移到mFreeSlots。
状态: DEQUEUED —> FREE

producer->cancelBuffer()

描述: 给定1个slot,仅仅将该slot从mActiveBuffers迁移到mFreeBuffers,不对buffer进行任何操作。
状态: DEQUEUED —> FREE

consumer->attachBuffer()

描述: 给定1个GraphicBuffer指针,优先从mFreeSlots中获取一个slot,并与该GraphicBuffer绑定。slot从mFreeSlots/mFreeBuffers迁移到mActiveBuffers。
状态: FREE —> ACQUIRED

consumer->detachBuffer()

描述: 给定1个slot,将该slot绑定的buffer解绑。slot从mActiveBuffers迁移到mFreeSlots。
状态: ACQUIRED —> FREE

consumer->discardFreeBuffers

描述: 将mFreeBuffers中的slot全部迁移到mFreeSlots中,并释放所有绑定的buffers。
状态: FREE —> FREE

光看上面这些描述,可能大家还是不太清楚这些特殊函数的意义和用途。那么通过下面与基本函数的对比,我想大家应该会更容易理解一些。

Producer:

函数名区别
attachBuffer不涉及到buffer的分配动作
dequeueBuffer可能会涉及到buffer的分配动作
函数名区别
detachBuffer释放buffer,slot —> mFreeSlots
cancelBuffer不释放buffer,slot —> mFreeBuffers

Consumer:

函数名区别
attachBuffer直接从FREE —> ACQUIRED
acquireBuffer必须是 QUEUED —> ACQUIRED
函数名区别
detachBuffer释放buffer,slot —> mFreeSlots
releaseBuffer不释放buffer,slot —> mFreeBuffers

六、跨进程如何实现

《最简单的BufferQueue测试程序(三)》中,演示了如何跨进程操作BufferQueue。在该示例中我们看到,client.cpp中对BufferQueue的远程操作代码,和《(一)》中本地执行的代码一模一样。但是前者是通过binder调用来实现跨进程访问,而后者则为本地函数直接调用,虽然二者在代码的表现形式上都是一样的,但是各自实现的路径却是有差别的。当然,殊途同归,最终BufferQueue的操作结果还是一样的

那么client.cpp中同样的代码,是如何实现跨进程通信的呢?

Binder 调用

client端:remote()->transact()
server端:onTransact()
在这里插入图片描述

  1. client端通过 remote()->transact() 对binder驱动发起操作请求,并等待返回结果。
  2. binder驱动将该请求转发给对应的server端。
  3. server端通过onTransact()来响应client的操作请求,并将结果返回给client端。

虚函数

如下图所示:
在这里插入图片描述

Class Base 为基类,Class A 和 Class B 均继承于 Class Base。由于Class Base内部为纯虚函数,因此不能直接对它进行实例化,只能对它的子类 Class A 或 Class B 进行实例化对象。

有时候因为软件设计需要,我们希望使用同一套代码来实现不同的软件逻辑。例如,我们希望当实现 Class A 对象时,则调用 Class A 的成员函数。当实现 Class B 对象时,则调用 Class B 的成员函数。那么这时候,只需要定义一个基类 Class Base 的指针 sp,让它分别指向不同的子类对象,就可以实现。

Bp 和 Bn

Bp: Binder Proxy,Binder远程代理,即client端。
Bn: Binder Native,Binder本地实现,即server端。

如果将上面两幅图融合在一起,就成了下面这样:
在这里插入图片描述

于是,我们只需要定义一个基类的指针 sp,就可以使用同一套操作接口,来实现不同的操作路径。比如 sp->func1(),当sp指向 Class BpBase 对象时,它执行的是binder远程调用;当sp指向 Class BnBase 对象时,它执行的是本地函数func1()的直接调用。

因此,如果仅仅只看下面这段代码,是无法区分该BufferQueue的操作是在本地进程执行,还是在其它进程中执行的。

void main(void)
{
    sp<IGraphicBufferProducer> producer;
    sp<IGraphicBufferConsumer> consumer;
	...
	producer->dequeueBuffer();
	producer->requestBuffer();
	producer->queueBuffer();
	...
	consumer->acquireBuffer();
	consumer->releaseBuffer();
}

唯一区分的方法是,看producer和consumer所指向的对象,是在本地进程创建的,还是在远程进程中创建的。

七、总结

  1. buffer状态切换如下:
    在这里插入图片描述

  2. 除了关注代码的逻辑,还需要确定当前是本地操作,还是远程调用;

  3. Android 规定,BufferQueue只能在Consumer进程中创建;

八、参考资料

示例代码下载:GitHub BufferQueue
Android自带BufferQueue测试程序:BufferQueue_test.cpp
玛法里奥赵四 优快云博客:AndroidFramework – Binder中的Bn与Bp

### BufferQueue in Android Framework In the context of the Android framework, `BufferQueue` plays a crucial role in graphics rendering pipelines. This component facilitates communication between producers (such as renderers or video decoders) and consumers (like SurfaceFlinger). The primary function is to manage buffers that contain graphical data. The producer-consumer model implemented by `BufferQueue` ensures efficient buffer management without requiring direct interaction between components[^1]. A key feature of this mechanism involves asynchronous operations where one entity produces graphic content while another consumes it for display purposes. To achieve seamless performance, `BufferQueue` supports double buffering techniques which allow continuous updates even when frames are being processed simultaneously. For developers working within the Android OS environment, understanding how `BufferQueue` operates can lead to more optimized applications especially those involving multimedia playback or complex UI animations. #### Implementation Details To integrate `BufferQueue`, developers typically interact through higher-level APIs provided by frameworks such as SurfaceView or TextureView rather than directly manipulating internal structures. However, under-the-hood mechanisms involve several important classes: - **IGraphicBufferProducer**: Interface used by producers to queue buffers into `BufferQueue`. - **IGraphicBufferConsumer**: Interface utilized by consumers like SurfaceFliner to dequeue buffers from `BufferQueue`. These interfaces ensure proper synchronization between different parts of an application ensuring smooth operation during screen refresh cycles. ```java // Example code showing basic setup using BufferQueue related methods. import android.view.Surface; public class MySurface { private final Surface mSurface; public void setDefaultBufferSize(int width, int height){ mSurface.setDefaultBufferSize(width, height); } } ```
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

何小龙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值