[Android禅修之路] 解读SurfaceFlinger中的BufferQueue
theme: channing-cyan highlight: androidstudio
解读BufferQueue
前言
在之前的文章中, 我们介绍了合成的过程中, 用到的生产者和消费者的设计模式, 并且还提到了一个缓冲区, 这篇文章, 就来详细说明一下这个缓冲区, 究竟是一个什么东西。
首先,我们先看官方提供的说明图。
众所周知, SurfaceFlinger 使用了生产者和消费者模型, 那么这个生产者和消费者之间的数据是如何封装和传递的呢? 答案就是 BufferQueue , 接下来我们就来聊聊什么是 BufferQueue 和它究竟有什么作用,最后再研究一下这个生产者和消费者模型是如何运作的。
首先我们看生产者的缓冲区是如何提供出来的, 之前在SurfaceFlinger 合成中的工作我们看到了获取缓冲区的逻辑, 是调用 Surface.cpp 中的GraphicBufferProducer 的 dequeueBuffer, 而这里的 GraphicBufferProducer 其实是一个 BpGraphicBufferProducer, 它用到了 Binder 进行进程通信, 今天我们就来看看它的具体实现
一 BufferQueue是什么
首先,我们看看这个 BufferQueue 是什么。当然,要看它是什么,最先看的当然是看这个类的定义和官方提供的注释。
```cpp class BufferQueue { public: // Buffer 的最大绑定数 enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
// 当 Slot 没有和绑定时的枚举值
enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT };
// IGraphicBufferConsumer.h 头文件中的别名
enum {
NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
PRESENT_LATER = IGraphicBufferConsumer::PRESENT_LATER,
};
// 在异步模式下,保留两个 Slot 以保证生产者和消费者可以异步运行。
enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
typedef ::android::ConsumerListener ConsumerListener;
// 一个 ConsumerListener 的实现, 它保持了一个 ConsumerListener 的弱引用
// 它可用将调用转发到消费者对象
// 使用它可用避免 BufferQueue 和生产者 Consumer 的循环引用
class ProxyConsumerListener : public BnConsumerListener {
public:
explicit ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
~ProxyConsumerListener() override;
void onDisconnect() override;
void onFrameAvailable(const BufferItem& item) override;
void onFrameReplaced(const BufferItem& item) override;
void onBuffersReleased() override;
void onSidebandStreamChanged() override;
void addAndGetFrameTimestamps(
const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) override;
private:
// IConsumerListener 的弱引用
wp<ConsumerListener> mConsumerListener;
};
// 生产者和消费者使用的 Slot 是由 BufferQueue 进行管理的, 而缓冲区的分配则是由 allocator 进行的
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
bool consumerIsSurfaceFlinger = false);
}; ```
BufferQueue 这个类比较简单,不过还是先介绍一下这里及以后出现的一些名词或者概念。
- NUM_BUFFER_SLOTS:BufferSlot 数组的最大长度,数量是32。(BufferSlot 对应的翻译统一为缓冲槽)
- INVALID_BUFFER_SLOT:BufferSlot 绑定状态的枚举值,默认是 INVALID_BUFFER_SLOT,也就是没有对应的缓冲槽。
- ConsumerListener:消费者的监听器,它用于将调用转发给消费者。
因为 BufferQueue 中只有一个 createBufferQueue 函数,所以接下来我们就来看一下 createBufferQueue 函数的实现。
1.1 createBufferQueue
```cpp
void BufferQueue::createBufferQueue(sp
sp<BufferQueueCore> core(new BufferQueueCore());
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core, consumerIsSurfaceFlinger));
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
*outProducer = producer;
*outConsumer = consumer;
} ```
这个函数也是非常的简单, 它就创建了三个对象
- BufferQueueCore: 这个就是 BufferQueue 中真正工作的对象
- BufferQueueProducer: 生产者对象
- BufferQueueConsumer: 消费者对象
1.2 BufferQueueCore
如果我们看过生产者 BufferQueueProducer::dequeueBuffer 中的代码, 我们就会见到 mCore 这个对象, 它就是 BufferQueueCore
对于 BufferQueueCore 的定义,这里就不放代码了,不过 BufferQueueCore 中比较重要的一处就是它里面维护了4个数组,现在我们就来看一下 BufferQueueCore 中保存的这些 buffer 数组对象, 还有它们的作用
1.3 BufferQueueCore 维护的数组
BufferQueueCore中维护了4个数组, 这些数组保存的对象是 BufferSlot, 它们分别如下
cpp
// mFreeSlots 包含所有 Free 状态的且当前没有绑定 buffer 的 slots
std::set<int> mFreeSlots;
// mFreeBuffers 包含所有 Free 状态的且当前绑定了 buffer 的 slots
std::list<int> mFreeBuffers;
// mUnusedSlots 包含所有 Free 状态且没有绑定 buffer 且未使用的 slots.
std::list<int> mUnusedSlots;
// mActiveBuffers 包含了当前所有绑定了非 Free 状态 buffer 的 slots
std::set<int> mActiveBuffers;
这里我们只是简单列举出这4个数组,后续用到它们的时候,我们在进一步说明。接下来我们来看看和这些数组对应的缓冲槽 BufferSlot 对象。
二 BufferSlot
BufferSlot 这个对象的结构体比较短, 这里就全部列举出来了
```cpp struct BufferSlot {
BufferSlot()
: mGraphicBuffer(nullptr), // 一个缓冲槽对应一个图形缓冲区 GraphicBuffer
mEglDisplay(EGL_NO_DISPLAY),
mBufferState(),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
mFence(Fence::NO_FENCE),
mAcquireCalled(false),
mNeedsReallocation(false) {
}
// mGraphicBuffer 指向为此 Slot 分配的 GraphicBuffer, 如果没有分配则为 NULL
sp<GraphicBuffer> mGraphicBuffer;
// EGLDisplay , 用于创建 EGLSyncKHR
EGLDisplay mEglDisplay;
// 当前 buffer slot 的状态
BufferState mBufferState;
// 告知 procducer 是否被调用 requestBuffer
// 用于 debug 和查找bug
bool mRequestBufferCalled;
// mFrameNumber 是此 slot 的 Frame 队列编号, 这是用于 buffer 队列的 LRU排序
// 因为 buffer 可能这发出 release fence 信号之前 release
uint64_t mFrameNumber;
// mEglFence是EGL sync对象,它关联的 slot 必须在 dequeue 之前发出信号
// 在创建 buffer 的时候它会被初始化为 EGL_NO_SYNC_KHR , 并且它可以设置为 releaseBuffer 中的新同步对象
EGLSyncKHR mEglFence;
// mFence 说一个围栏, 当之前的 buffer 的所有者完成启动工作时, 它就会发出信号
// 当这个 buffer 是 Free 状态时, 这个围栏会指示消费者何时完成 buffer 的读取工作
// 或者指示当生产者何时已经完成写入, 如果它在写入数据数据后调用了 cancelBuffer
// 当这个 buffer 是 QUEUED 状态时, 它指示生产者何时完成 buffer 的填充
// 当这个 buffer 是 DEQUEUED/ACQUIRED 时, 这个围栏会和 buffer 一起传递给生产者或者消费者,并且设置为 NO_FENCE
sp<Fence> mFence;
// 指示消费者是否看到了此缓冲区
bool mAcquireCalled;
// Indicates whether the buffer was re-allocated without notifying the producer.
If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued to prevent the producer from using a stale cached buffer.
如果是这样,它需要在退出队列时设置缓冲区需要重新分配标志,以防止生产者使用过时的缓存缓冲区。
// 指示是否在没有通知生产者的情况下重新分配了 buffer
// 如果是的, 那么当这个 buffer 出队时它需要设置为 BUFFER_NEEDS_REALLOCATION , 以防止生产者使用了一个过时的 buffer
bool mNeedsReallocation;
}; ```
BufferSlot 这个类里面定义的东西虽然比较多,但是都很容易理解,这里也不需要全部记住,如果后续遇到,再回过头重新查看即可。唯独有一个需要说明的是这里面和 Fence 相关对象,
Fence 是 Android 的图形系统中,用于处理缓冲区同步用的,之前已经说过了,Android 的图形系统中,用到了生产者和消费者模型,所以必定涉及到生产者和消费者之前的同步,并且因为这里的生产者和消费者模式不在同一个进程,甚至涉及到 CPU 和 GPU 的同步,所以弄了一套 Fence 机制,关于 Fence 机制,这里就不多介绍了,详细的可以看 [解读Fence]-(未完成) 。这里我们假设已经了解了 Fence 机制,当然不了解也没有关系,看完本篇文章之后,再去了解 Fence 机制,然后再回来验证一遍,会更加理解 BufferQueue 的工作原理。
这里我们先只关注里面的 BufferState 这个对象。
2.1 BufferState
首先, 我们先看 BufferState 的结构体定义, 里面的方法比较简单, 主要就是定义了5个状态值。
```cpp // BufferState 是跟踪缓冲区 slot 运行的状态。 struct BufferState {
// 所有的 slot 最初都是 Free 状态
BufferState()
: mDequeueCount(0),
mQueueCount(0),
mAcquireCount(0),
mShared(false) {
}
uint32_t mDequeueCount;
uint32_t mQueueCount;
uint32_t mAcquireCount;
bool mShared;
// 一个缓冲区可用处于5种状态, 它们分别是
//
// | mShared | mDequeueCount | mQueueCount | mAcquireCount |
// --------|---------|---------------|-------------|---------------|
// FREE | false | 0 | 0 | 0 |
// DEQUEUED| false | 1 | 0 | 0 |
// QUEUED | false | 0 | 1 | 0 |
// ACQUIRED| false | 0 | 0 | 1 |
// SHARED | true | any | any | any |
// FREE : 标识这个缓冲区是可以出队给生产者使用, 此缓冲区对应的 slot 属于 BufferQueue ,
// 当它被调用 dequeueBuffer 时, 状态会转变为 DEQUEUED
// DEQUEUED : 表示此缓冲区已经通过生产者出队, 但是还没有入队或者取消, 一旦发出相关释放围栏的信号,生产者就可以修改缓冲区的内容。
// 此缓冲区对应的 slot 所有者是生产者, 当调用 queueBuffer/attachBuffer 时状态就会变成 QUEUED ,
// 当调用 cancelBuffer/detachBuffer 时,状态就会变成 FREE
// QUEUED : 表示缓冲区已经被生产者填充, 可以入队提供给消费者使用
// 缓冲区的内容可能会继续改变, 所以这相关的 fence 信号发出前,此缓冲区对应 slot 的拥有者是 BufferQueue
// 当调用 acquireBuffer 时状态会转变为 ACQUIRED ,
// 或者在异步模式下另一个 buffer 入队时, 此 buffer 的状态会转变为 FREE
// ACQUIRED : 表示这个 buffer 已经被消费者获得, 但是和 QUEUED 状态一样,在获取 fence 信号之前,消费者不得访问内容。
// 这个缓冲区对应的 slot 的所有者是消费者, 在调用 releaseBuffer/detachBuffer 时会转变为 FREE
// 一个独立的缓冲区也可以通过调用 attachBuffer 进入 ACQUIRED 状态
// SHARED : 表示此缓冲区处于共享模式中, 它可以和其他的几种状态组合(但是不能和FREE状态组合),
// 它也可以多次的入队/出队/获取
inline bool isFree() const {
return !isAcquired() && !isDequeued() && !isQueued();
}
inline bool isDequeued() const {
return mDequeueCount > 0;
}
inline bool isQueued() const {
return mQueueCount > 0;
}
inline bool isAcquired() const {
return mAcquireCount > 0;
}
inline bool isShared() const {
return mShared;
}
inline void reset() {
*this = BufferState();
}
const char* string() const;
inline void dequeue() {
mDequeueCount++;
}
inline void detachProducer() {
if (mDequeueCount > 0) {
mDequeueCount--;
}
}
inline void attachProducer() {
mDequeueCount++;
}
inline void queue() {
if (mDequeueCount > 0) {
mDequeueCount--;
}
mQueueCount++;
}
inline void cancel() {
if (mDequeueCount > 0) {
mDequeueCount--;
}
}
inline void freeQueued() {
if (mQueueCount > 0) {
mQueueCount--;
}
}
inline void acquire() {
if (mQueueCount > 0) {
mQueueCount--;
}
mAcquireCount++;
}
inline void acquireNotInQueue() {
mAcquireCount++;
}
inline void release() {
if (mAcquireCount > 0) {
mAcquireCount--;
}
}
inline void detachConsumer() {
if (mAcquireCount > 0) {
mAcquireCount--;
}
}
inline void attachConsumer() {
mAcquireCount++;
}
}; ```
对照着Google官方提供的 BufferQueue 缓冲区的流转图,接下来简单说明一下一个 BufferSlot 的一生
- BufferSlot 的出生, 每个 BufferSlot 出生时都是 FREE 状态, 它的所有者是 BufferQueue
- 生产者调用 dequeue , BufferSlot 出队, 此时 BufferSlot 的状态转变为 DEQUEUED , 它的所有者是生产者
- DEQUEUED 状态下, 生产者会将内容填充到 BufferSlot 对应的缓冲区中
- 生产者也可以调用 cancelBuffer/detachBuffer , 之后缓冲区的状态就会转变为 FREE
- 生产者填充完数据后,调用 queue 将 BufferSlot 入队, 此时 BufferSlot 的状态转变为 QUEUED , BufferSlot 的所有者为 BufferQueue
- 消费者调用 acquire 获取 BufferQueue 中的 BufferSlot, 此时 BufferSlot 的状态转变为 ACQUIRED , BufferSlot 的所有者为消费者
- 消费者读取完数据后,调用 release 释放 BufferSlot, 之后 BufferSlot 的状态又会重新变成 FREE , BufferSlot 的所有者为 BufferQueue
注意:这里所有的缓冲区流转,我们只说了所有权,在图形系统中,缓冲区除了所有权,还有一个使用权。我们可以把他理解为一个图书馆,不同的人来借书,借书的人有使用权,但是没有书的所有权。
在理解了这些概念之后,我们顺着生产者和消费者模型的顺序,看一遍 BufferQueue 的工作流程
三 dequeueBuffer
首先从生产者向 BufferQueue 申请 BufferSlot 开始,这个函数是 BufferQueueProducer::dequeueBuffer。因为这个函数较长,所以我们将它拆成几个部分看。
3.1 dequeueBuffer 的第一部分
```cpp
status_t BufferQueueProducer::dequeueBuffer(int outSlot, sp
{
std::lock_guard<std::mutex> lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
// 首先判断缓冲区的状态是否弃用,mIsAbandoned 初始化的值为 false
// 在 consumerDisconnect 中它会被修改为 true
if (mCore->mIsAbandoned) {
return NO_INIT;
}
// 接着判断缓冲区是否和 Surface 建立了连接, 如果没有也直接返回
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
return NO_INIT;
}
}
// 判断生产者需要的宽高是否合法,要么全为0,要么全不为0
if ((width && !height) || (!width && height)) {
return BAD_VALUE;
}
// 设置参数的默认值
status_t returnFlags = NO_ERROR;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false;
{
std::unique_lock<std::mutex> lock(mCore->mMutex);
// 如果当前没有空闲的 Buffer , 但是当前正在分配, 则等待分配完成, 而不去重复分配
if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
mDequeueWaitingForAllocation = true;
mCore->waitWhileAllocatingLocked(lock);
mDequeueWaitingForAllocation = false;
mDequeueWaitingForAllocationCondition.notify_all();
}
if (format == 0) {
format = mCore->mDefaultBufferFormat;
}
// 开启消费者请求的标志
usage |= mCore->mConsumerUsageBits;
//如果宽和高都是0, 则使用默认的宽高
const bool useDefaultSize = !width && !height;
if (useDefaultSize) {
width = mCore->mDefaultWidth;
height = mCore->mDefaultHeight;
}
int found = BufferItem::INVALID_BUFFER_SLOT;
....
} ```
注意,这里系统只是判断了 mFreeBuffer 数组,如果 mFreeBuffer 数组为空,并且 mIsAllocating 为 true,就会调用 waitWhileAllocatingLocked 陷入等待。
SurfaceFlinger 中使用的缓冲区是有数量限制的,如果当前处于正在分配缓冲区的状态,就会导致阻塞。而 mIsAllocating 表示是否正在分配,如果是正在分配,则不会重复进行分配,而是进入阻塞。
接下来我们看第二部分的代码。
3.2 dequeueBuffer 的第二部分
```cpp
status_t BufferQueueProducer::dequeueBuffer(int outSlot, sp
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
// 正常情况是不能执行到此处的代码
return -EBUSY;
}
// 注意,将找到的 BufferSlot 对应的图形缓冲区赋值给了 buffer
// 我们之前在介绍 BufferSlot 时说过,一个 BufferSlot 对应一个图形缓冲区(GraphicBuffer)
// 但是并没有说这个缓冲区现在已经分配好了,也就是说这个缓冲区还可能并没有真正分配
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
// 如果我们现在不允许分配新的缓冲区
if (!mCore->mAllowAllocation) {
// buffer 又需要一个缓冲区
if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
// 如果是共享缓冲区模式就直接返回了,一般不是此模式
if (mCore->mSharedBufferSlot == found) {
return BAD_VALUE;
}
// 找到的 found 没有缓冲区,但是现在又要缓冲区,那咋办嘛,重新找呗
// 只好把 found 放回去,再重新找吧。注意,这里放进的是 mFreeSlots 数组
mCore->mFreeSlots.insert(found);
mCore->clearBufferSlotLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
}
}
}
// 执行完 while 循环,那么 found 要么已经有了缓冲区,要么是可以分配缓冲区
// 还是老样子,不管有没有,先放进去
// 把 while 中找到的 mGraphicBuffer 的地址赋值给 buffer
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if (mCore->mSharedBufferSlot == found &&
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
// 还是先判断共享缓冲区模式
return BAD_VALUE;
}
// 现在找到的这个 found 肯定是我们要用的了,先放到 mActiveBuffers 数组
// [1.3]中介绍了4个数组分别存放什么类型的 BufferSlot
if (mCore->mSharedBufferSlot != found) {
mCore->mActiveBuffers.insert(found);
}
// 将找到的结果保存
*outSlot = found;
} ```
第二部分主要就是 mSlots 的查找过程,这部分代码已经加了详细的注释。查找过程就是通过调用 waitForFreeSlotThenRelock 实现的,这个查找的具体过程我们后面再看。先看 dequeueBuffer 的第三部分的代码
3.3 dequeueBuffer 的第三部分
```cpp ... *outSlot = found;
attachedByConsumer = mSlots[found].mNeedsReallocation;
mSlots[found].mNeedsReallocation = false;
// 修改找到 Buffer 的状态,还记得 Google 的官方图吗
mSlots[found].mBufferState.dequeue();
// 现在看这个 buffer 是已经有了,还是没有需要重新分配
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;
// returnFlags 标志打开需要重新分配的开关
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} else {
// 设置 mBufferAge,这将是该缓冲区排队时的帧编号
mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
}
eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
// 不要在共享缓冲区模式下返回 Fence,第一帧除外
// 在查找 BufferSlot 的时候,还会通过返回参数拿到一个 Fence,
// 这个 Fence 决定了这个 BufferSlot 的上一任主人有没有处理完事情,也就是说当前的主人有没有使用权
*outFence = (mCore->mSharedBufferMode &&
mCore->mSharedBufferSlot == found) ?
Fence::NO_FENCE : mSlots[found].mFence;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
// 如果启用了共享缓冲区模式,缓存的第一个出队的slot,标记为共享缓冲区
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
BufferQueueCore::INVALID_BUFFER_SLOT) {
mCore->mSharedBufferSlot = found;
mSlots[found].mBufferState.mShared = true;
}
} // Autolock scope ```
第三部分是查找到了 mSlots 之后,对 mSlots 状态的更新。如果查找到的 BufferSlot 需要重新分配缓冲区,那么就会初始化 BufferSlot 的成员变量
3.4 dequeueBuffer 的第四部分
```cpp
// 如果 BUFFER_NEEDS_REALLOCATION 的标志开启,就创建一个新的 GraphicBuffer 对象
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
sp
status_t error = graphicBuffer->initCheck();
{
std::lock_guard<std::mutex> lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
// 将创建的缓冲区放进 BufferSlot
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
}
mCore->mIsAllocating = false;
// 还记得第一部分的阻塞吗,如果正在分配,就阻塞,这里进行唤醒操作
mCore->mIsAllocatingCondition.notify_all();
if (error != NO_ERROR) {
mCore->mFreeSlots.insert(*outSlot);
mCore->clearBufferSlotLocked(*outSlot);
return error;
}
if (mCore->mIsAbandoned) {
mCore->mFreeSlots.insert(*outSlot);
mCore->clearBufferSlotLocked(*outSlot);
return NO_INIT;
}
}
}
if (attachedByConsumer) { returnFlags |= BUFFER_NEEDS_REALLOCATION; }
if (eglFence != EGL_NO_SYNC_KHR) { EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0, 1000000000); // 如果出现问题,记录错误,但返回缓冲区而不同步访问它。 此时中止出队操作为时已晚。 eglDestroySyncKHR(eglDisplay, eglFence); }
if (outBufferAge) { // 返回帧编号 *outBufferAge = mCore->mBufferAge; } // addAndGetFrameTimestamps(nullptr, outTimestamps);
return returnFlags; ```
第四部分的代码也比较简单,唯一需要注意的就是 GraphicBuffer 的创建和内存分配。不过 GraphicBuffer 的内存分配其实是一个非常复杂的过程,这里就不深入了,详细的可以看[解读GraphicBuffer] todo
接下来,就来看看 BufferSlot 的查找过程 waitForFreeSlotThenRelock
四 BufferSlot 的查找
4.1 waitForFreeSlotThenRelock
waitForFreeSlotThenRelock 这个函数比较简单, 它主要通过两个函数来获取缓冲区
- getFreeBufferLocked: 它是从 FreeBuffer 数组中获取缓冲区
- getFreeSlotLocked: 它是从 FreeSlot 数组中获取缓冲区
```cpp
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
std::unique_lock
int dequeuedCount = 0;
int acquiredCount = 0;
// 统计 ActiveBuffers 中 DEQUEUED 和 ACQUIRED 的 BufferSlot 的数量
for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isDequeued()) {
++dequeuedCount;
}
if (mSlots[s].mBufferState.isAcquired()) {
++acquiredCount;
}
}
// 当缓冲区入队时,检查生产者的 Dequeue 的 BufferSlot 数量是否超过了 mMaxDequeuedBufferCount
if (mCore->mBufferHasBeenQueued &&
dequeuedCount >= mCore->mMaxDequeuedBufferCount) {
return INVALID_OPERATION;
}
// 上面都是统计和一些参数判断,现在才真正开始查找
// 还是老样子,初始化一个值
*found = BufferQueueCore::INVALID_BUFFER_SLOT;
// 如果我们快速断开和重新连接,我们可能会处于一种状态,即slot是空的,
// 但队列中有很多缓冲区。这可能导致我们在超过使用者时耗尽内存。如果看起来有太多缓冲区在排队,则等待
// 先拿到最大的 Buffer 数量,一般是双缓冲的2,或者三缓冲的3
const int maxBufferCount = mCore->getMaxBufferCountLocked();
// 如果入队的 BufferSlot 太多,就会 tooManyBuffers
bool tooManyBuffers = mCore->mQueue.size()
> static_cast<size_t>(maxBufferCount);
if (tooManyBuffers) {
} else {
// 如果是共享缓冲区模式并且存在共享缓冲区,则使用共享缓冲区
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = mCore->mSharedBufferSlot;
} else {
if (caller == FreeSlotCaller::Dequeue) {
// 如果调用的是出队 Dequeue , 则优先使用 FreeBuffer
// 因为 FreeBuffer 中的 Slot 已经和 buffer 进行过绑定
// 这样就不需要重新分配 buffer
int slot = getFreeBufferLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else if (mCore->mAllowAllocation) {
*found = getFreeSlotLocked();
}
} else {
// 如果调用的是 attach , 则优先使用 FreeSlot
int slot = getFreeSlotLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else {
*found = getFreeBufferLocked();
}
}
}
}
// 如果没有找到缓冲区, 或者是 tooManyBuffers, 就再试一次
tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
tooManyBuffers;
if (tryAgain) {
if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
// 如果是非阻塞模式则直接返回错误(生产者和消费者由应用程序控制)
return WOULD_BLOCK;
}
// 等待锁, 如果超时就返回, 如果没有超时, 就重试
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;
} ```
虽然 waitForFreeSlotThenRelock 调用的是 BufferQueueProducer , 但是真正的实现是 BufferQueueCore。具体的查找过程可以分为以下几步
- dequeueBuffer() 优先从 mFreeBuffers 中获取一个 BufferSlot。
- 如果 mFreeBuffers 为空,则从 mFreeSlots 中获取 BufferSlot,并为它分配一块指定大小的 buffer。
- 将其对应的 BufferSlot 状态从 FREE 修改为 DEQUEUED,然后将该 slot 从 mFreeSlots 迁移到 mActiveBuffers 中。
- 将获取到的 slot 作为出参返回给调用者。如果该 BufferSlot 绑定的 GraphicBuffer 是重新分配的,则返回值为 BUFFER_NEEDS_REALLOCATION,否则为 NO_ERROR
当我们使用 dequeueBuffer 获取 BufferSlot 的时候,会优先从 FreeBuffer 数组中获取缓冲区,因为它里面是 Slot 已经和 buffer 进行了绑定,这样我们就可以省去 buffer 的申请和绑定的过程,当然,如果 FreeBuffer 数组为空,或者没有可用的 buffer,那么就会从 FreeSlot 数组中申请,这种情况下,还会进行一个 GraphicBuffer 的申请和绑定的过程,这些逻辑在 dequeueBuffer 已经看到了。
另外,在 waitForFreeSlotThenRelock 中,我们还看到一种情况,就是 tooManyBuffers,那么什么情况下回出现 tooManyBuffers 呢?就拿比较常用的三缓冲来说吧,一个缓冲区用来给生产者写,一个缓冲区用来给消费者显示,还有一个缓冲区用来给 SurfaceFlinger 合成。那么如果这时候生产者使劲的申请缓冲区写内容,并且将写好的 GraphicBuffer 放进 BufferQueue,而消费者根本来不及消费,就会导致 tooManyBuffers 出现了。
当然,正常情况一般是生产者生成内容不及时,绘制时间过长,导致 GraphicBuffer 没有及时传递给 BufferQueue,导致显示丢帧,但是看系统源码我们发现,生产者生成过慢不行,生成过快同样也是不行的。
4.2 getFreeBufferLocked和getFreeSlotLocked
在 waitForFreeSlotThenRelock 中,我们还看到了 getFreeBufferLocked 和 getFreeSlotLocked 这两个函数。不过它们都比较简单, 这里就不过多介绍了
```cpp int BufferQueueProducer::getFreeBufferLocked() const { if (mCore->mFreeBuffers.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = mCore->mFreeBuffers.front(); mCore->mFreeBuffers.pop_front(); return slot; }
int BufferQueueProducer::getFreeSlotLocked() const { if (mCore->mFreeSlots.empty()) { return BufferQueueCore::INVALID_BUFFER_SLOT; } int slot = *(mCore->mFreeSlots.begin()); mCore->mFreeSlots.erase(slot); return slot; } ```
到这里,dequeueBuffer 的基本流程就已经介绍完了,至于生产者拿到了 BufferSlot 做了哪些,并不是本文的重点,接下来我们再看看当生产者完成生产之后,BufferSlot 的入队操作。
五 queueBuffer
queueBuffer 是生产者完成了生产之后,将 BufferSlot 转交给 BufferQueue 的过程。这个函数我们也它拆成几个部分看。
5.1 queueBuffer 的第一部分
```cpp status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) {
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 中的变量取出,保存到 requestedPresentTimestamp 等等的传入参数中
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
if (acquireFence == nullptr) {
return BAD_VALUE;
}
auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);
// 缩放模式,就是当 Buffer 内容的大小和屏幕的代销不符的时候,如何处理,如果没有设置则直接返回
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:
return BAD_VALUE;
}
// 定义了一些变量
sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
uint64_t currentFrameNumber = 0;
// 定义一个 BufferItem
BufferItem item;
} ```
第一部分的代码也比较简单
- 定义一系列变量准备给后面用
- input 是一个 QueueBufferInput 结构体,里面封装了 BufferSlot 相关的一系列变量,这里将 input 中的变量取出来
- scalingMode 是 BufferSlot 的缩放模式,就是当 Buffer 内容的大小和屏幕的代销不符的时候,如何处理,这个需要设置,如果没有设置会直接返回 BAD_VALUE
- 定义了一个 BufferItem 对象
5.2 queueBuffer 的第二部分
```cpp
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
BufferItem item;
{
std::lock_guard
// 如果刚刚启用了共享缓冲区模式,缓冲区的第一个缓冲槽已入队,就将其标记为共享缓冲区
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
BufferQueueCore::INVALID_BUFFER_SLOT) {
mCore->mSharedBufferSlot = slot;
mSlots[slot].mBufferState.mShared = true;
}
// 将 BufferSlot 的 GraphicBuffer 保存到 graphicBuffer
const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
// 拿到 GraphicBuffer 的宽高
Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
// 定义裁剪区域
Rect croppedRect(Rect::EMPTY_RECT);
// 裁剪 bufferRect 中处于裁剪区域的内容
crop.intersect(bufferRect, &croppedRect);
// 裁剪之后 croppedRect 就是 crop
if (croppedRect != crop) {
return BAD_VALUE;
}
// 如果 dataSpace 为 Unknown 则设置为默认值
if (dataSpace == HAL_DATASPACE_UNKNOWN) {
dataSpace = mCore->mDefaultBufferDataSpace;
}
// 传递 Fence,后续消费者根据这个来判断是否有使用权
mSlots[slot].mFence = acquireFence;
// 修改 BufferSlot 的状态为 QUEUE
mSlots[slot].mBufferState.queue();
// 增加帧计数器并将其本地版本存储在 mCore->mMutex 上的锁外部使用
++mCore->mFrameCounter;
currentFrameNumber = mCore->mFrameCounter;
mSlots[slot].mFrameNumber = currentFrameNumber;
// 将参数封装进 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;
item.mIsDroppable = mCore->mAsyncMode ||
(mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
(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;
mStickyTransform = stickyTransform;
...
```
第二部分代码也比较简单,就是构造了一个 BufferItem,将传递进来的 input 里的值设置到 BufferItem 中。这里唯一需要注意的是对 GraphicBuffer 内容进行的裁剪,因为 GraphicBuffer 有可能超出要显示的区域,所以需要裁剪出满足条件的内容。
cpp
bool Rect::intersect(const Rect& with, Rect* result) const {
result->left = max(left, with.left);
result->top = max(top, with.top);
result->right = min(right, with.right);
result->bottom = min(bottom, with.bottom);
return !(result->isEmpty());
}
5.3 queueBuffer 的第三部分
```cpp status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ...
mStickyTransform = stickyTransform;
// 缓存共享缓冲区数据,以便可以重新创建 BufferItem
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;
// 将 BufferItem 入队,这里分为几种情况
if (mCore->mQueue.empty()) {
// 当 BufferQueue 队列为空,我们可以忽略 MDEQUEUEBUFERCANNOTBLOCK,将此缓冲区入队
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();
// After leaving shared buffer mode, the shared buffer will still be around. Mark it as no longer shared if this operation causes it to be free.
// 离开共享缓冲区模式后,共享缓冲区仍然存在。如果此操作导致其空闲,则将其标记为不再共享
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;
}
}
// 如果最后一个缓冲区是可覆盖的,则用传入缓冲区覆盖它
mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
frameReplacedListener = mCore->mConsumerListener;
} else {
// 如果最后一个缓冲区不是可覆盖的,则直接入队
mCore->mQueue.push_back(item);
frameAvailableListener = mCore->mConsumerListener;
}
}
// 修改 BufferQueue 的变量,发送通知
mCore->mBufferHasBeenQueued = true; // BufferQueue 是否已经有 Buffer 入队
mCore->mDequeueCondition.notify_all(); // 通知 mDequeueCondition 锁相关的线程
mCore->mLastQueuedSlot = slot; // 保存 slot 到 mLastQueuedSlot
// 将要返回的参数保存到 output
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
output->transformHint = mCore->mTransformHint;
// 现在待消费的 Buffer
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
// 下一帧的编号
output->nextFrameNumber = mCore->mFrameCounter + 1;
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
// 获取回调函数的票证
callbackTicket = mNextCallbackTicket++;
}
```
第三部分则是将创建的 BufferItem 入队。这里入队会分为几种情况分别处理
- BufferQueue 为空,则直接入队
- BufferQueue 不为空
- 最后一帧的内容是否可覆盖,如果是可覆盖的,就丢弃最后一帧的内容,将新的 BufferItem 入队
- 如果最后一帧的内容不可覆盖,则将新 BufferItem 入队
然后修改 BufferQueue 的变量,并发出通知。还记得在 dequeueBuffer 中陷入等待的几种情况吗[见3.3],如果找不到 BufferSlot 或者出现 tooManyBuffer 的情况就会让线程陷入等待。说到底就是生产者申请 Buffer 太快了。
5.4 queueBuffer 的第四部分
```cpp status_t BufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output) { ... // 当使用者是 SurfaceFinger 时,可以不清除 GraphicBuffer, // 因为可以保证 BufferQueue 在 SurfaceFinger 的进程内,并且不会有绑定器调用 if (!mConsumerIsSurfaceFlinger) { item.mGraphicBuffer.clear(); }
// 回调虽然没有持有没有主缓冲队列锁,但是持有对应的回调锁,所以也可以确保回调是有序的
int connectedApi;
sp<Fence> lastQueuedFence;
{
std::unique_lock<std::mutex> lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(lock);
}
// 通知监听器有 BufferSlot 可以进行消费了
if (frameAvailableListener != nullptr) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != nullptr) {
frameReplacedListener->onFrameReplaced(item);
}
connectedApi = mCore->mConnectedApi;
lastQueuedFence = std::move(mLastQueueBufferFence);
mLastQueueBufferFence = std::move(acquireFence);
mLastQueuedCrop = item.mCrop;
mLastQueuedTransform = item.mTransform;
++mCurrentCallbackTicket;
mCallbackCondition.notify_all();
}
// 更新并获取FrameEventHistory
nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
NewFrameEventsEntry newFrameEventsEntry = {
currentFrameNumber,
postedTime,
requestedPresentTimestamp,
std::move(acquireFenceTime)
};
// 做一个记录
addAndGetFrameTimestamps(&newFrameEventsEntry,
getFrameTimestamps ? &output->frameTimestamps : nullptr);
// 无锁等待
if (connectedApi == NATIVE_WINDOW_API_EGL) {
// 在此处等待允许两个已满的缓冲区入队,但不允许第三个缓冲区入队。
lastQueuedFence->waitForever("Throttling EGL Production");
}
return NO_ERROR;
} ```
第四部分主要就是进行一些回调和通知了,并且定了了一些 mLast 相关的变量。
5.5 queueBuffer 总结
相比于 dequeueBuffer,queueBuffer 的逻辑简单太多了,就是将生产者传入的一些参数解析后,创建一个对应的 BufferItem 入队到 BufferQueue 中,然后根据最后一帧是否可覆盖分别处理,然后修改一些状态。最后回调通知。
六 acquireBuffer
看完生产者 BufferQueueProducer 的两个函数,接下来我们再看看消费者 BufferQueueConsumer 的两个函数,首先是 acquireBuffer。
6.1 acquireBuffer 的第一部分
```cpp status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber) {
int numDroppedBuffers = 0;
sp<IProducerListener> listener;
{
std::unique_lock<std::mutex> lock(mCore->mMutex);
// 先计算已经 Acquired 的 Buffer
int numAcquiredBuffers = 0;
for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isAcquired()) {
++numAcquiredBuffers;
}
}
// 如果已经 Acquired 的 Buffer 超出最大数 mMaxAcquiredBufferCount 的限制就返回
// 这里的最大数允许超出1个,是为了消费者可以使用新的 Buffer 替换旧的 Buffer
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
return INVALID_OPERATION;
}
bool sharedBufferAvailable = mCore->mSharedBufferMode &&
mCore->mAutoRefresh && mCore->mSharedBufferSlot !=
BufferQueueCore::INVALID_BUFFER_SLOT;
// 在异步模式下,列表保证是一个缓冲区深,而在同步模式下,我们使用最旧的缓冲区。
if (mCore->mQueue.empty() && !sharedBufferAvailable) {
return NO_BUFFER_AVAILABLE;
}
// 获取 BufferQueue 队列的迭代器
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
} ```
第一部分的逻辑比较简单
- 首先判断当前 acquire 的缓冲区是否已经超过了可以 acquire 的最大缓冲区数量,一般情况下这个数量是1,不过这里会在这个数量的基础上再加1,因为为了让消费者可以用新的缓冲区替换旧的缓冲区
- 非共享模式,如果缓冲区队列是空,就返回没有可用缓冲区
6.2 acquireBuffer 的第二部分
```cpp status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber) { ... // 如果指定了 expectedPresent,我们可能还不想返回缓冲区。 // 如果它被指定并且有多个缓冲区排队,我们可能想要删除一个缓冲区。 // 如果我们处于共享缓冲区模式并且队列为空,则跳过此步骤,因为在这种情况下我们将只返回共享缓冲区。 if (expectedPresent != 0 && !mCore->mQueue.empty()) { // expectedPresent 参数表示这个 buffer 是期望被什么时候显示到屏幕上 // 如果 buffer 的 desiredPresent 时间早于 expectedPresent 时间,那么这个 buffer 就会按时显示或者尽快显示 // 如果想显示它,就 acquire 并且返回它,如果在 expectedPresent 时间之前都不想显示它,就返回 PRESENT_LATER // 注意:代码假定来自系统时钟的单调时间值为正。
// 首先检查我们是否可以丢帧。 如果时间戳是由 Surface 自动生成的,我们将跳过此检查。
// 如果应用程序没有明确生成时间戳,则它可能不希望基于它们的时间戳丢弃帧。
// mIsAutoTimestamp 代表这个缓冲区是否有自动生成的时间戳
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
const BufferItem& bufferItem(mCore->mQueue[1]);
// 如果删除 entry[0] 会给我们留下一个消费者尚未准备好的缓冲区,请不要删除它。
if (maxFrameNumber && bufferItem.mFrameNumber > maxFrameNumber) {
break;
}
// 如果 entry[1] 是及时的,则删除 entry[0](并重复)。 我们在这里应用了一个额外的标准:如果我们的 desiredPresent 在 expectedPresent 的 +/- 1 秒内,我们只删除较早的缓冲区。
// 否则,虚假的 desiredPresent 时间(例如,0 或较小的相对时间戳),通常意味着“忽略时间戳并立即获取”,将导致我们丢帧。
// 如果 entry[1] 的栅栏尚未发出信号,则不要删除较早的缓冲区。
if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
desiredPresent > expectedPresent) {
// 这个缓冲区设置为在不久的将来显示,或者 desiredPresent 是垃圾。 无论哪种方式,我们都不想为了更快地将其显示在屏幕上而丢弃前一个缓冲区。
break;
}
if (!front->mIsStale) {
// 队首的缓冲区仍在 mSlots 中,因此将插槽标记为空闲
mSlots[front->mSlot].mBufferState.freeQueued();
// 离开共享缓冲区模式后,共享缓冲区仍然存在。 如果此操作导致其免费,则将其标记为不再共享。
if (!mCore->mSharedBufferMode &&
mSlots[front->mSlot].mBufferState.isFree()) {
mSlots[front->mSlot].mBufferState.mShared = false;
}
// 不要将共享缓冲区放在空闲列表中
if (!mSlots[front->mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(front->mSlot);
mCore->mFreeBuffers.push_back(front->mSlot);
}
listener = mCore->mConnectedProducerListener;
++numDroppedBuffers;
}
mCore->mQueue.erase(front);
front = mCore->mQueue.begin();
}
// 查看是否准备好获取前端缓冲区
nsecs_t desiredPresent = front->mTimestamp;
bool bufferIsDue = desiredPresent <= expectedPresent ||
desiredPresent > expectedPresent + MAX_REASONABLE_NSEC;
bool consumerIsReady = maxFrameNumber > 0 ?
front->mFrameNumber <= maxFrameNumber : true;
if (!bufferIsDue || !consumerIsReady) {
return PRESENT_LATER;
}
}
```
第二部分主要是判断旧的缓冲区是否可以被新缓冲区替代。如果旧的缓冲区有自动生成的时间戳,并且新缓冲区也马上就要显示了,那么这种情况下,会直接使用新的缓冲区,如果新的缓冲区还要很久才会被用到,那么就使用旧的缓冲区。
6.3 acquireBuffer 的第三部分
```cpp status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, nsecs_t expectedPresent, uint64_t maxFrameNumber) {
int slot = BufferQueueCore::INVALID_BUFFER_SLOT;
if (sharedBufferAvailable && mCore->mQueue.empty()) {
// 在获取缓冲区之前确保缓冲区已完成分配
mCore->waitWhileAllocatingLocked(lock);
slot = mCore->mSharedBufferSlot;
// 从上次排队时缓存的数据为共享缓冲区重新创建 BufferItem。
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
outBuffer->mFence = Fence::NO_FENCE;
outBuffer->mFenceTime = FenceTime::NO_FENCE;
outBuffer->mCrop = mCore->mSharedBufferCache.crop;
outBuffer->mTransform = mCore->mSharedBufferCache.transform &
~static_cast<uint32_t>(
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
outBuffer->mScalingMode = mCore->mSharedBufferCache.scalingMode;
outBuffer->mDataSpace = mCore->mSharedBufferCache.dataspace;
outBuffer->mFrameNumber = mCore->mFrameCounter;
outBuffer->mSlot = slot;
outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled;
outBuffer->mTransformToDisplayInverse =
(mCore->mSharedBufferCache.transform &
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
outBuffer->mSurfaceDamage = Region::INVALID_REGION;
outBuffer->mQueuedBuffer = false;
outBuffer->mIsStale = false;
outBuffer->mAutoRefresh = mCore->mSharedBufferMode &&
mCore->mAutoRefresh;
} else {
slot = front->mSlot;
*outBuffer = *front;
}
if (!outBuffer->mIsStale) {
mSlots[slot].mAcquireCalled = true;
// 如果 BufferItem 之前不在队列中,则不要减少队列计数。 当队列为空并且上面创建了 BufferItem 时,会在共享缓冲区模式下发生这种情况。
if (mCore->mQueue.empty()) {
mSlots[slot].mBufferState.acquireNotInQueue();
} else {
mSlots[slot].mBufferState.acquire();
}
mSlots[slot].mFence = Fence::NO_FENCE;
}
// 如果缓冲区先前已被消费者获取,则将 mGraphicBuffer 设置为 NULL 以避免在消费者端不必要地重新映射此缓冲区
if (outBuffer->mAcquireCalled) {
outBuffer->mGraphicBuffer = nullptr;
}
mCore->mQueue.erase(front);
// 我们可能在丢弃旧缓冲区时释放了一个 Slot,或者生产者可能被阻塞
mCore->mDequeueCondition.notify_all();
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
}
// 回调
if (listener != nullptr) {
for (int i = 0; i < numDroppedBuffers; ++i) {
listener->onBufferReleased();
}
}
return NO_ERROR;
} ```
6.4 acquireBuffer 总结
和生产者想必,消费者的逻辑要简单很多,acquireBuffer 的主要工作如下
- 首先判断已经处于 ACQUIRED 状态缓冲区的数量,如果数量超出了限制,就不会返回新的缓冲区。不过这里会在最大数量的基础上加1,为了做新旧缓冲区的替换
- 判断缓冲区是否有自动生成的时间戳,这个时间戳用于丢弃旧的缓冲区。然后基于这个时间戳再判断旧缓冲区是否可以直接使用新缓冲区替代
- 将确定好的缓冲区返回出去,并且变更 Slot 的状态为 ACQUIRED,通知其他阻塞的线程,然后调用回调函数。
七 releaseBuffer
```cpp
status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
const sp
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
releaseFence == nullptr) {
// slot 不合法或者 releaseFence 为空
return BAD_VALUE;
}
sp<IProducerListener> listener;
{
std::lock_guard<std::mutex> lock(mCore->mMutex);
// 如果帧的序号因为缓冲区重新分配而改变,我们可以忽略旧缓冲区 releaseBuffer。
// 对于共享缓冲区,请忽略这一点,其中由于缓冲区正在排队并同时获取,帧号很容易不同步。
if (frameNumber != mSlots[slot].mFrameNumber &&
!mSlots[slot].mBufferState.isShared()) {
return STALE_BUFFER_SLOT;
}
// 判断 Slot 的状态,Slot 的状态必须是 ACQUIRED
if (!mSlots[slot].mBufferState.isAcquired()) {
return BAD_VALUE;
}
mSlots[slot].mEglDisplay = eglDisplay;
mSlots[slot].mEglFence = eglFence;
mSlots[slot].mFence = releaseFence;
mSlots[slot].mBufferState.release();
// 离开共享缓冲区模式后,共享缓冲区仍然存在。 如果此操作导致其 FREE,则将其标记为不再共享。
if (!mCore->mSharedBufferMode && mSlots[slot].mBufferState.isFree()) {
mSlots[slot].mBufferState.mShared = false;
}
// 不要将共享缓冲区放在 FREE 列表中
if (!mSlots[slot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(slot);
mCore->mFreeBuffers.push_back(slot);
}
listener = mCore->mConnectedProducerListener;
mCore->mDequeueCondition.notify_all();
}
// 无锁回调
if (listener != nullptr) {
listener->onBufferReleased();
}
return NO_ERROR;
} ```
releaseBuffer 就是消费者使用完缓冲区后,释放缓冲区的过程。在调用 releaseBuffer 函数之后,mSlot 的状态就会变成 FREE 状态。
八 总结
SurfaceFlinger 中 BufferQueue 的工作流程和原理到此就基本理清了,联系之前的 SurfaceFlinger 合成的工作流程,SurfaceFlinger 合成工作中,最关键的流程已经基本学习完毕了。当然,对于 SurfaceFlinger 工作来说,这连冰山一角都算不上,前路漫漫。
- Activity启动源码解析(Android12)
- 从MediaServer看Binder的使用方式(一)
- 从MediaServer看Binder的使用方式(二)
- [Android禅修之路] 解读Layer
- [Android禅修之路] Android图形系统,从Activity到Surface
- [Android禅修之路] 解读 GraphicBuffer 之 Framework 层
- [Android禅修之路] 解读SurfaceFlinger中的BufferQueue
- [Android禅修之路] SurfaceFlinger 合成中的工作
- [Android禅修之路] SurfaceFlinger 中的一些对象
- [Android禅修之路] SurfaceFlinger 合成前的预处理
- [Android禅修之路] SurfaceFlinger合成总览
- [Android禅修之路] SurfaceFlinger的启动过程
- [Android禅修之路] Android 图形系统开篇