6 BufferQueue

上一篇已经说到,BufferQueue是SurfaceFlinger管理和消费surface的中介,我们就开始分析bufferqueue。

每个应用 可以由几个BufferQueue?

应用绘制UI 所需的内存从何而来?

应用和SurfaceFlinger 如何互斥共享资源的访问?

6.1 Buffer的状态

const char* BufferSlot::bufferStateName(BufferState state) {
switch (state) {
case BufferSlot::DEQUEUED: return "DEQUEUED";
case BufferSlot::QUEUED: return "QUEUED";
case BufferSlot::FREE: return "FREE";
case BufferSlot::ACQUIRED: return "ACQUIRED";
default: return "Unknown";
}
}

状态变迁如下:FREE->DEQUEUED->QUEUED->ACQUIRED->FREE

BufferQueue的状态迁移图:

我们先来看,producer & comsumer 分别是什么?
应用程序需要刷新UI,所以就会产生surface到BufferQueue,so,producer 可以认为就是应用程序,也可以是上一篇里面介绍的ISurfaceComposerClient。
comsumer不用看也知道,就是SurfaceFlinger。所以可以明确一个大致的流程就是,
1)应用需要刷新UI,获取一个buffer的缓冲区,这个操作就是dequeue。经过dequeue以后,该buffer被producer锁定,其他Owner就不能插手了。
2)然后把surface写入到该buffer里面,当producer认为写入结束后,就执行queue的操作,把buffer 归还给bufferqueue。Owner也变成为bufferqueue。
3)当一段buffer里面由数据以后,comsumer就会收到消息,然后去获取该buffer。
void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const android::BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) {
listener->onFrameAvailable(item);
}
}

bufferqueue里面就是comsumerlistener,当有可以使用的buffer后,就会通知comsumer使用。

// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;

可以看到注释,bufferqueue的mSlot[64],并不是都有内容的,也就是mSlot存的是buferr的指针,如果没有,就存null

slot的个数在andorid5.0 里面定义在BufferQueueDefs.h里面,

 enum { NUM_BUFFER_SLOTS =  };

6.2 Buffer内存的出处

既然producer是主动操作,所以如果在dequeue的时候,已经获取了内存,后面的操作也就不需要分配内存了。

status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
sp<android::Fence> *outFence, bool async,
uint32_t width, uint32_t height, uint32_t format, uint32_t usage) {
ATRACE_CALL();
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
} // Autolock scope BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x",
async ? "true" : "false", width, height, format, usage); if ((width && !height) || (!width && height)) {
BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
return BAD_VALUE;
} status_t returnFlags = NO_ERROR;
EGLDisplay eglDisplay = EGL_NO_DISPLAY;
EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
bool attachedByConsumer = false; { // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(); if (format == ) {
format = mCore->mDefaultBufferFormat;
} // Enable the usage bits the consumer requested
usage |= mCore->mConsumerUsageBits; int found;
status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
&found, &returnFlags);
if (status != NO_ERROR) {
return status;
} // This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
BQ_LOGE("dequeueBuffer: no available buffer slots");
return -EBUSY;
} *outSlot = found;
ATRACE_BUFFER_INDEX(found); attachedByConsumer = mSlots[found].mAttachedByConsumer; const bool useDefaultSize = !width && !height;
if (useDefaultSize) {
width = mCore->mDefaultWidth;
height = mCore->mDefaultHeight;
} mSlots[found].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if ((buffer == NULL) ||
(static_cast<uint32_t>(buffer->width) != width) ||
(static_cast<uint32_t>(buffer->height) != height) ||
(static_cast<uint32_t>(buffer->format) != format) ||
((static_cast<uint32_t>(buffer->usage) & usage) != usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
mSlots[found].mRequestBufferCalled = false;
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE; returnFlags |= BUFFER_NEEDS_REALLOCATION;
} if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
"slot=%d w=%d h=%d format=%u",
found, buffer->width, buffer->height, buffer->format);
} eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
*outFence = mSlots[found].mFence;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
} // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
status_t error;
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
width, height, format, usage, &error));
if (graphicBuffer == NULL) {
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
return error;
} { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) {
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
} mSlots[*outSlot].mFrameNumber = UINT32_MAX;
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope
} if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} if (eglFence != EGL_NO_SYNC_KHR) {
EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, ,
);
// If something goes wrong, log the error, but return the buffer without
// synchronizing access to it. It's too late at this point to abort the
// dequeue operation.
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);
} BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
*outSlot,
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); return returnFlags;
}

dequeueBuffer

step1:BufferQueueProducer::waitForFreeSlotThenRelock 循环的主要作用就是查找可以使用的slot。

step2:释放不需要的buffer,并且统计已分配的内存。

        // Free up any buffers that are in slots beyond the max buffer count
for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
assert(mSlots[s].mBufferState == BufferSlot::FREE);
if (mSlots[s].mGraphicBuffer != NULL) {
mCore->freeBufferLocked(s);
*returnFlags |= RELEASE_ALL_BUFFERS;
}
}
for (int s = ; s < maxBufferCount; ++s) {
switch (mSlots[s].mBufferState) {
case BufferSlot::DEQUEUED:
++dequeuedCount;
break;
case BufferSlot::ACQUIRED:
++acquiredCount;
break;
case BufferSlot::FREE:
// We return the oldest of the free buffers to avoid
// stalling the producer if possible, since the consumer
// may still have pending reads of in-flight buffers
if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) {
*found = s;
}
break;
default:
break;
}
}

如果有合适的,found 就是可以使用的buffer编号。

如果dequeue too many,but comsumer还来不及消耗掉,这个时候,有可能会导致OOM,所以,判断是否在队列里面有过多的buffer。

等待comsumer消耗后,释放互斥锁。

        if (tryAgain) {
// Return an error if we're in non-blocking mode (producer and
// consumer are controlled by the application).
// However, the consumer is allowed to briefly acquire an extra
// buffer (which could cause us to have to wait here), which is
// okay, since it is only used to implement an atomic acquire +
// release (e.g., in GLConsumer::updateTexImage())
if (mCore->mDequeueBufferCannotBlock &&
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
return WOULD_BLOCK;
}
mCore->mDequeueCondition.wait(mCore->mMutex);
}

在返回dqueueBuffer这个方法:如果没有找到free的slot,就直接返回错误。当然正常情况下是不会发生的。

        // This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
BQ_LOGE("dequeueBuffer: no available buffer slots");
return -EBUSY;
}
 mSlots[found].mBufferState = BufferSlot::DEQUEUED;

把找到的buffer的状态设为DEQUEUE。

在判断了mSlot[found]的属性以后,它可能是空的,也有可能不符合当前需要的buffer的size,就给mSlot[found]分配新的属性和内存

if ((buffer == NULL) ||
(static_cast<uint32_t>(buffer->width) != width) ||
(static_cast<uint32_t>(buffer->height) != height) ||
(static_cast<uint32_t>(buffer->format) != format) ||
((static_cast<uint32_t>(buffer->usage) & usage) != usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = NULL;
mSlots[found].mRequestBufferCalled = false;
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE; returnFlags |= BUFFER_NEEDS_REALLOCATION;
} if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
"slot=%d w=%d h=%d format=%u",
found, buffer->width, buffer->height, buffer->format);
} eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
*outFence = mSlots[found].mFence;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
} // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
status_t error;
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
width, height, format, usage, &error));
if (graphicBuffer == NULL) {
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
return error;
} { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) {
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
} mSlots[*outSlot].mFrameNumber = UINT32_MAX;
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope
}

这样buffer对应的内存就是在producer,dequeue操作的时候分配内存的。(if need)

6.3应用程序和BufferQueue的关系

首先看一张surface各类之间的关系:

 这里多了一个Layer的东西,Layer代表一个画面的图层(在android app的角度以前是没有图层的概念的,虽然framework有)。
SurfaceFlinger混合,就是把所有的layer做混合。
我们先来看createlayer:
status_t SurfaceFlinger::createLayer(
const String8& name,
const sp<Client>& client,
uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp)

这里关键是handler & gbp这2个参数。

我们看下去代码:layer 是从createNormalLayer里面来的

status_t SurfaceFlinger::createNormalLayer(const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
// initialize the surfaces
switch (format) {
case PIXEL_FORMAT_TRANSPARENT:
case PIXEL_FORMAT_TRANSLUCENT:
format = PIXEL_FORMAT_RGBA_8888;
break;
case PIXEL_FORMAT_OPAQUE:
format = PIXEL_FORMAT_RGBX_8888;
break;
} *outLayer = new Layer(this, client, name, w, h, flags);
status_t err = (*outLayer)->setBuffers(w, h, format, flags);
if (err == NO_ERROR) {
*handle = (*outLayer)->getHandle();
*gbp = (*outLayer)->
getProducer();
} ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
return err;
}

producer跟踪源代码可以看到:

class MonitoredProducer : public IGraphicBufferProducer 

通过bind机制,可以认为就是BufferQueue的一个子类。

所以,每一个bufferqueue对应的都是一个layer。

看下handler:

sp<IBinder> Layer::getHandle() {
Mutex::Autolock _l(mLock); LOG_ALWAYS_FATAL_IF(mHasSurface,
"Layer::getHandle() has already been called"); mHasSurface = true; /*
* The layer handle is just a BBinder object passed to the client
* (remote process) -- we don't keep any reference on our side such that
* the dtor is called when the remote side let go of its reference.
*
* LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for
* this layer when the handle is destroyed.
*/ class Handle : public BBinder, public LayerCleaner {
wp<const Layer> mOwner;
public:
Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer)
: LayerCleaner(flinger, layer), mOwner(layer) {
}
}; return new Handle(mFlinger, this);
}

没有什么东西,就是LayerCleaner,

它的设计目的就是SurfaceFlinger来清除图层。

所以我们可以得出结论

1)一个app对应一个surfaceFlinger,可以有多个layer,从而对应多个bufferqueue

2)surface的缓冲区内存是BufferQueue在进行dequeue的时候分配的,属于client端。

3)App & SurfaceFlinger都通过bufferQueue来分配和使用缓冲区,所以互斥操作是由BufferQueue来实现。

参考:

《深入理解android内核设计思想》 林学森

  

android Gui系统之SurfaceFlinger(2)---BufferQueue的更多相关文章

  1. android Gui系统之SurfaceFlinger(3)---SurfaceFlinger

    7.SurfaceFlinger SurfaceFlinger在前面的篇幅了,多有涉及. SurfaceFlinger是GUI刷新UI的核心,所以任何关于SurfaceFlinger的改进都会对and ...

  2. android Gui系统之SurfaceFlinger(1)---SurfaceFlinger概论

    GUI 是任何系统都很重要的一块. android GUI大体分为4大块. 1)SurfaceFlinger 2)WMS 3)View机制 4)InputMethod 这块内容非常之多,但是理解后,可 ...

  3. android Gui系统之SurfaceFlinger(1)---SurfaceFlinger概论【转】

    转自:https://www.cnblogs.com/deman/p/5584198.html 阅读目录 1.OpenGL & OpenGL ES 2.Android的硬件接口HAL 3.An ...

  4. android Gui系统之SurfaceFlinger(4)---Vsync(1)

    8.Vsync 8.1概论 VSYNC(Vertical Synchronization)是一个相当古老的概念,对于游戏玩家,它有一个更加大名鼎鼎的中文名字—-垂直同步. “垂直同步(vsync)”指 ...

  5. android Gui系统之SurfaceFlinger(5)---Vsync(2)

    9.Vsync第二部分 在上一篇中我们讲到,视图的刷新需要很多步骤, void SurfaceFlinger::handleMessageRefresh() { ATRACE_CALL(); preC ...

  6. Android GUI系统

    图解Android - Android GUI 系统 (1) - 概论 图解Android - Android GUI 系统 (2) - 窗口管理系统 图解Android - Android GUI ...

  7. 图解Android - System Service 概论 和 Android GUI 系统

    通过 图解Android - Binder 和 Service 一文中,我们已经分析了Binder 和 Service的工作原理.接下来,我们来简要分析Android 系统里面都有哪些重要的Servi ...

  8. 图解Android - Android GUI 系统 (1) - 概论

    Android的GUI系统是Android最重要也最复杂的系统之一.它包括以下部分: 窗口和图形系统 - Window and View Manager System. 显示合成系统 - Surfac ...

  9. 图解Android - Android GUI 系统 (2) - 窗口管理 (View, Canvas, Window Manager)

    Android 的窗口管理系统 (View, Canvas, WindowManager) 在图解Android - Zygote 和 System Server 启动分析一 文里,我们已经知道And ...

随机推荐

  1. 基于MVC4+EasyUI的Web开发框架经验总结(5)--使用HTML编辑控件CKEditor和CKFinder

    Web开发上有很多HTML的编辑控件,如CKEditor.kindeditor等等,很多都做的很好,本文主要介绍在MVC界面里面,CKEditor的配置和使用.CKEditor的前身是FCKEdito ...

  2. 慎用Assembly.LoadFile()和Assembly.LoadFrom()

    经测这俩方法会锁住文件,导致程序运行期间无法对load过的程序集文件进行更名/删除/覆盖等等操作,考虑用Assembly.Load()文件字节组替代: Assembly.Load(File.ReadA ...

  3. 15天玩转redis —— 第七篇 同事的一次缓存操作引起对慢查询的认识

    上个星期同事做一个业务模块,需要将一个80M的数据存入到redis缓存中,想法总是好的,真操作的时候遇到了HSet超时,我们使用的是C#的 StackExchange.Redis驱动. <red ...

  4. Win10 IoT C#开发 2 - 创建基于XAML的UI程序 及 应用的三种部署方法

    Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本不同,是为物联网设备专门设计的,硬件也不仅仅限于x86架构,同时可以在ARM架构上运行. 上一章我 ...

  5. 图-最短路径-Dijktra(迪杰斯特拉)算法

    1. 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉算法于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以起始 ...

  6. Java就业指导

    想要成为合格的Java程序员或工程师到底需要具备哪些专业技能,面试者在面试之前到底需要准备哪些东西呢?本文陈列的这些内容既可以作为个人简历中的内容,也可以作为面试的时候跟面试官聊的东西,你可以把这些内 ...

  7. iOS AFNetworking 打印从服务器返回的错误提示信息

    每次做项目的时候都会在网络请求时候测试接口的时候会出现一些不同的错误,而控制台打印的错误提示信息都是data类型,看不出提示的错误的信息是什么.后面经过一些查阅发现其实是可以把这个转变为string的 ...

  8. 硬盘空间满导致mysql ibd文件被删后提示Tablespace is missing for table 'db_rsk/XXX"

    昨天一早,开发人员反馈说一个测试环境报Tablespace is missing for table 'db_rsk/XXX",周末刚升级过,特地让开发回去查了下,说脚本中肯定没有drop ...

  9. Nodejs与ES6系列1:变量声明

    1.声明变量 在JS当中一个变量的作用域(scope)是程序中定义这个变量的区域.变量分为两类,全局(global)的和局部的.其中全局变量的作用域是全局性的,即在JavaScript代码中,它处处都 ...

  10. 基于 jQuery 实现垂直滑动的手风琴效果

    今天我们要与大家分享一个漂亮而灵活的垂直 jQuery 手风琴效果.其主要思想是扩大手风琴片上的点击和显示更多的信息.其他内容片段将变得不那么透明.当使用一个导航箭头导航下一个片段,新的片会从顶部或底 ...