GraphicBuffer管理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GraphicBuffer管理相关的知识,希望对你有一定的参考价值。

参考技术A android系统定义了一个Native窗口 ANativeWindow,结构定义如下:

根据ANativeWindow的结构定义可看出,ANativeWindow描述了本地窗口的基本信息,同时还定义了一系列操作窗口缓冲区的方法,窗口是需要内容显示的,显示的内容自然是存放在窗口缓冲区ANativeWindowBuffer中。
来看下ANativeWindowBuffer窗口缓存的结构定义:

ANativeWindowBuffer定义了窗口缓冲区的基本信息,包括宽,高, stride(每行像素个数),图像格式等,还定义了handle指针,存放Gralloc模块分配的真正的Buffer的地址。

ANativeWindow代表了一个窗口,提供了窗口管理的基本方法来操作窗口缓冲区的出队和入队。ANativeWindowBuffer则负责描述Window一个图形缓冲区。

Surface实现了ANativeWindow, 负责管理一个窗口,而GraphicBuffer则实现了ANativeWindowBuffer,负责管理一个图形缓冲区。Surface后续在进行分析,我们先看下GraphicBuffer的实现。

GraphicBuffer继承自ANativeWindow

GraphicBuffer有三个构造函数
1:创建一个width × height的GraphicBuffer
2:根据已经有的buffer的handle来创建一个GraphicBuffer
3:根据一个ANativeWindowBuffer来创建一个GraphicBuffer
我们只分析第一个看看GraphicBuffer是如何创建的?

构造方法先将GraphicBuffer的width,height等属性设置为默认值0,然后调用initSize来创建Buffer.

initSize方法中首先获取了GraphicBufferAllocator对象, GraphicBufferAllocator实现是单例模式,所以每个进程中只有一个GraphicBufferAllocator负责Buffer的分配。
调用GraphicBufferAllocator的alloc方法为当前GraphicBuffer分配了一个指定宽高,以及Format的Buffer, 分配成功后将宽高,stride等属性保存到GraphicBuffer对象的属性中,把buffer的handle保存在GraphicBuffer的handle中,GraphicBuffer就可以管理一个真正的图形缓冲区Buffer,这样GraphicBuffer就创建好了。

GraphicBuffer在构造方法中调用了GraphicBufferAllocator来分配了一个真正的图形缓冲区,具体是怎么分配的呢?

在分析Gralloc模块的时候知道,Gralloc设备会在两个地方被打开,一个是HWCompser,另一个就是GraphicBufferAllocator, 此处会加载Gralloc硬件模块抽象库,同时调用gralloc_open来打开GRALLOC_HARDWARE_GPU0 设备,将返回的alloc_device_t保存在mAllocDev中。
接着看负责缓冲区分配的alloc方法。

alloc方法会调用打开的alloc_device_t设备的alloc方法,GraphicBufferAllocator中调用 mAllocDev->alloc方法会调用到gralloc模块,来分配图形缓冲区,如果分配成功之后则根据返回的属性创建一个alloc_rec_t对象,并将这个对象添加到sAllocList中。
sAllocList是GraphicBufferAllocator维护的一个列表,一个进程中所有分配的图形缓冲区都会维护在这个列表中,只有调用GraphicBufferAllocator的free方法释放缓冲区后,才会从列表中移除。

alloc_device_t设备在打开的过程中会注册操作图形缓冲区的方法,gralloc_alloc和gralloc_free,两个方法分别 用于分配和释放图形缓冲区。

该方法首先根据传入的图像格式计算每个像素占用的字节数,根据每个像素的字节数计算每行像素的个数stride 以及 图形缓冲区所需要的内存大小size。
最后由这些结果作为参数调用gralloc_alloc_buffer来分配缓冲区内存。

gralloc_alloc_buffer分配缓冲区内存做了什么操作呢?
1:根据参数传入的缓冲区大小 创建了一个匿名共享内存,共享内存fd存放到private_handle_t的fd中
2:调用mapBuffer,将共享内存映射到当前进程,并吧内存首地址保存到private_handle_t的base中
3:将private_handle_t返回给调用者,这个就是图形缓冲区的handle, 用于唯一区别一个缓冲区。

至此,Gralloc模块分配缓冲区的逻辑就分析完了

根据传入的图形缓冲区的private_handle_t标识符,调用terminateBuffer来释放buffer, 因我们分析GraphicBuffer的分配与释放,FB设备的相关逻辑暂时不关注。
直接看terminateBuffer是如何释放缓冲区的?

terminateBuffer 释放图形缓冲区很简单,根据private_handle_t的base属性判断内存是否已经映射过了,如果已经映射,则调用munmap方法取消映射即可。

GraphicBuffer实现了ANativeWindowBuffer,用来管理图像窗口的图形缓冲区,是ANativeWindow的显示内容和操作对象,GraphicBuffer的handle指针指向的才是真正的缓冲区内存。
GraphicBufferAllocator负责缓冲区内存的分配,会调用到gralloc模块的gralloc设备进行分配。每个图形缓冲区都是一个匿名共享内存空间,分配的时候创建一个匿名共享内存,然后映射到当前进程,释放的时候将匿名共享内存从当前进程取消映射。

Android GraphicBuffer-Fence

这里需要介绍一个伴随着GraphicBuffer的Fence,包括Fence的诞生,Fence的处理。

在前面介绍Surface的时候,提到过Surface会通过BufferQueueProducer申请GraphicBuffer,用作显存,所以我们这里看一下BufferQueueProducer/Consumer是怎么管理GraphicBuffer的。(这里有一个问题,是否可以脱离BufferQueueProducer/Consumer去使用GraphicBuffer?)

BufferQueueProducer(简称producer)和BufferQueueConsumer(简称consumer)公用BufferQueueCore(简称core);core里面有一个mSlots = struct BufferSlot[64];同时mMaxAcquiredBufferCount=1,mMaxDequeuedBufferCount=1,同时getMaxBufferCountLocked 为3或者2:

int BufferQueueCore::getMaxBufferCountLocked() const {
    int maxBufferCount = mMaxAcquiredBufferCount + mMaxDequeuedBufferCount +
            ((mAsyncMode || mDequeueBufferCannotBlock) ? 1 : 0);

    // limit maxBufferCount by mMaxBufferCount always
    maxBufferCount = std::min(mMaxBufferCount, maxBufferCount);

    return maxBufferCount;
}
//BufferQueueCore创建的时候会初始化mFreeSlots(最多3个元素), mUnusedSlots有64-3个元素

   int numStartingBuffers = getMaxBufferCountLocked();
   for (int s = 0; s < numStartingBuffers; s++) {
     mFreeSlots.insert(s);
   }
   for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;  s++){
     mUnusedSlots.push_front(s);
   }

在APP拿到BufferQueueProducer的时候,在绘制之前,会调用BufferQueueProducer.allocateBuffers,预先分配足够的GraphicBuffer,防止在draw的时候分配造成delay;

分配Buffer的数量和mFreeSlots元素数目相同,也就是3个:

void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
        PixelFormat format, uint64_t usage) {
         newBufferCount = mCore->mFreeSlots.size();
         。。。。。。
     //创建3个GraphicBuffer
for (size_t i = 0; i < newBufferCount; ++i) { sp<GraphicBuffer> graphicBuffer = new GraphicBuffer( allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT, allocUsage, allocName); status_t result = graphicBuffer->initCheck(); if (result != NO_ERROR) { BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format" " %u, usage %#" PRIx64 ")", width, height, format, usage); Mutex::Autolock lock(mCore->mMutex); mCore->mIsAllocating = false; mCore->mIsAllocatingCondition.broadcast(); return; } buffers.push_back(graphicBuffer); } 。。。。。
//将新创建的GraphicBuffer添加到mSlots数组中。注意mSlots的index,这个index很重要,mSlots中的数据是和index绑定的,类似于ID;其中的graphicBuffer可能会发生变化。
for (size_t i = 0; i < newBufferCount; ++i) { if (mCore->mFreeSlots.empty()) { BQ_LOGV("allocateBuffers: a slot was occupied while " "allocating. Dropping allocated buffer."); continue; } auto slot = mCore->mFreeSlots.begin(); mCore->clearBufferSlotLocked(*slot); // Clean up the slot first mSlots[*slot].mGraphicBuffer = buffers[i]; mSlots[*slot].mFence = Fence::NO_FENCE; // freeBufferLocked puts this slot on the free slots list. Since // we then attached a buffer, move the slot to free buffer list. mCore->mFreeBuffers.push_front(*slot); BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", *slot); // Make sure the erase is done after all uses of the slot // iterator since it will be invalid after this point. mCore->mFreeSlots.erase(slot); } }

下面看一下GraphicBuffer的创建流程:

技术图片

GraphicBuffer经过GrallocService->GrallocHal,在libgralloc里面分配;其中比较常见的方式是使用ION驱动进行分配,分配完毕之后还要为buffer分配Fence。

 

以上是关于GraphicBuffer管理的主要内容,如果未能解决你的问题,请参考以下文章

Android GraphicBuffer-Fence

Android GraphicBuffer-Fence

Android:GraphicBuffer 数据格式

Android中的GraphicBuffer同步机制-Fence

Android dump渲染和合成图层GraphicBuffer指南

Android 重学系列 GraphicBuffer的诞生