Android6.0 显示系统(三) 管理图像缓冲区

来源:互联网 发布:火车票网络订单号查询 编辑:程序博客网 时间:2024/06/05 20:16

上篇博客最后分析到MonitoredProducer对象,这个对象只是一个代理,真正实是BufferQueueProducer类,这个对象和BufferQueueCore有关联,可以管理最多达64块的缓冲区。Surface可以理解为一张画布,那么Surface为何要和一个缓冲区队列相关呢?在播放动画时,美妙至少要播放24帧画面才能形成比较真实的动画效果。而这些数据是通过cpu解码得到的,准备他们需要时间。对于图像显示设备而言,刷新周期是固定的,我们必须要在它需要数据的时候把数据准备好。视频播放的每一帧也需要在指定的时间播放,因此解码器会提前准备好一批数据,这些数据保存在解码器内存的缓冲区中,当时间到达是,解码器会把内部缓冲区的图像复制到Surface中,但是显示设备并不是立刻就把数据取走的,因此Surface也需要缓冲区来临时保存数据。


一、BufferQueueCore BufferQueueProducer BufferQueueConsumer

上篇博客在Layer的onFirstRef函数中,调用了下面函数,创建了3个对象BufferQueueCore BufferQueueProducer BufferQueueConsumer。其中BufferCore是核心,把BufferQueueProducer和BufferQueueConsumer对象连接在一起。

[cpp] view plain copy
  1. void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,  
  2.         sp<IGraphicBufferConsumer>* outConsumer,  
  3.         const sp<IGraphicBufferAlloc>& allocator) {  
  4.   
  5.     sp<BufferQueueCore> core(new BufferQueueCore(allocator));  
  6.     sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));  
  7.     sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));  
  8.   
  9.     *outProducer = producer;  
  10.     *outConsumer = consumer;  
  11. }  


1.1 生产者和core的联系

IGraphicBufferProducer 的大致接口如下,BufferQueueProducer类是接口IGraphicBufferProducer 的实现,使用BufferQueueProducer之前先要调用connect函数,使用结束后调用disconnect断开连接。

[cpp] view plain copy
  1. class IGraphicBufferProducer : public IInterface  
  2. {  
  3. public:  
  4.     virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0;  
  5.   
  6.     virtual status_t setBufferCount(int bufferCount) = 0;  
  7.   
  8.     virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, bool async,  
  9.             uint32_t w, uint32_t h, PixelFormat format, uint32_t usage) = 0;  
  10.   
  11.     virtual status_t detachBuffer(int slot) = 0;  
  12.   
  13.     virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,  
  14.             sp<Fence>* outFence) = 0;  
  15.   
  16.     virtual status_t attachBuffer(int* outSlot,  
  17.             const sp<GraphicBuffer>& buffer) = 0;  
  18.   
  19.     virtual status_t queueBuffer(int slot,  
  20.             const QueueBufferInput& input, QueueBufferOutput* output) = 0;  
  21.     virtual void cancelBuffer(int slot, const sp<Fence>& fence) = 0;  
  22.     virtual int query(int what, int* value) = 0;  
  23.     virtual status_t connect(const sp<IProducerListener>& listener,  
  24.             int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;  
  25.   
  26.     virtual status_t disconnect(int api) = 0;  
  27.     virtual status_t setSidebandStream(const sp<NativeHandle>& stream) = 0;  
  28.     virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,  
  29.             PixelFormat format, uint32_t usage) = 0;  
  30.     virtual status_t allowAllocation(bool allow) = 0;  
  31.   
  32.     virtual status_t setGenerationNumber(uint32_t generationNumber) = 0;  
  33.   
  34.     virtual String8 getConsumerName() const = 0;  
  35. };  

在BufferQueueCore类中定义了一个64项的数据mSlots。

[cpp] view plain copy
  1. BufferQueueDefs::SlotsType mSlots;  


[cpp] view plain copy
  1. typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];  

每个缓冲区的类型是BufferSlot类型。它有两个重要的成员变量,mGraphicBuffer是指向图像缓冲区GraphicBuffer的指针,mBufferState表示图像缓冲区的状态。

[cpp] view plain copy
  1. sp<GraphicBuffer> mGraphicBuffer;  
  2. ......  
  3. BufferState mBufferState;  

BufferState的状态有下面几个

[cpp] view plain copy
  1. enum BufferState {  
  2.     FREE = 0,//空闲  
  3.     DEQUEUED = 1,//生产状态,被生产者拥有  
  4.     QUEUED = 2,//保存数据状态,被BufferQueue拥有  
  5.     ACQUIRED = 3//消费状态,被消费者拥有  
  6. };  


BufferQueueProducer的dequeueBuffer函数用来向BufferQueueCore申请一个空闲的slot,这个slot可能已经有缓冲区,也可能没有,如果没有缓冲区,dequeueBuffer函数会分配一块新的缓冲区。得到空闲的slot后,还需要调用requestBuffer函数来取得一块缓冲区。得到缓冲区,如果不需要了,可以使用cancelBuffer函数来释放这个slot。调用dequeueBuffer函数之后,缓冲区的拥有者是生产者,缓冲区处于DEQUEUED状态。一旦缓冲区复制数据完成,通过queueBuffer函数把缓冲区的控制权交还给BufferQueueCore,这时候缓冲区将处于QUEUED状态


1.2 消费者和core的联系

下面是IGraphicBufferComsumer接口的几个主要函数:

[cpp] view plain copy
  1. virtual status_t acquireBuffer(BufferItem* outBuffer,  
  2.         nsecs_t expectedPresent, uint64_t maxFrameNumber = 0) override;  
  3. ......  
  4.   
  5. virtual status_t releaseBuffer(int slot, uint64_t frameNumber,  
  6.         const sp<Fence>& releaseFence, EGLDisplay display,  
  7.         EGLSyncKHR fence);  
  8.   
  9.   
  10. virtual status_t connect(const sp<IConsumerListener>& consumerListener,  
  11.         bool controlledByApp);  
  12. virtual status_t disconnect();  
BufferQueueConsumer类是接口IGraphicBufferComsumer的实现,在使用它之前,先要调用connect函数建立联系,这里传递的参数是IConsumerListener对象,是一个回调接口,如果BufferQueue中有数据准备好了就会调用它的onFrameAvailable函数来通知消费者取走数据。

取走数据的时候,需要调用acquireBuffer函数,将缓冲区状态变成ACQUIRED,使用完之后调用releaseBuffer函数可以吧缓冲区数据归还给BufferQueueCore,这样缓冲区就变成FREE。

1.3 三者联系

对象BufferQueueProducer和BufferQueueConsumer好像没有直接联系,其实都是通过共同的BufferQueueCore对象连接在一起的,很多操作时直接使用BufferQueueCore对象的成员变量而不是函数来完成的。


二、GraphicBuffer对象的创建

对Surface而言,图像缓冲区是一个重要的数据结构,它是用户进程和图像显示器之间的纽带,下面我们来看看Surface的图像缓冲区是如何创建的。


2.1 内存缓冲区的创建

前面介绍了过dequeueBuffer函数,图像缓冲区GraphicBuffer就是在这个函数中创建的,当从BufferQueueCore中获取到空间的slot时,如果这个slot没有缓冲区就要新建一个。

下面是dequeueBuffer函数的部分代码,在从BufferQueueCore中获取到slot的时候,如果需要重新分配图像缓冲区就会调用mCore->mAllocator->createGraphicBuffer函数来重新创建一个图像缓冲区。

[cpp] view plain copy
  1.     ......  
  2.     *outSlot = found;//found复制到outslot  
  3.     ATRACE_BUFFER_INDEX(found);  
  4.   
  5.     attachedByConsumer = mSlots[found].mAttachedByConsumer;  
  6.   
  7.     mSlots[found].mBufferState = BufferSlot::DEQUEUED;//slot的状态修改变成生产状态  
  8.   
  9.     const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);  
  10.     if ((buffer == NULL) ||//为空,或者需要重新分配  
  11.             buffer->needsReallocation(width, height, format, usage))  
  12.     {  
  13.         mSlots[found].mAcquireCalled = false;  
  14.         mSlots[found].mGraphicBuffer = NULL;  
  15.         mSlots[found].mRequestBufferCalled = false;  
  16.         mSlots[found].mEglDisplay = EGL_NO_DISPLAY;  
  17.         mSlots[found].mEglFence = EGL_NO_SYNC_KHR;  
  18.         mSlots[found].mFence = Fence::NO_FENCE;  
  19.         mCore->mBufferAge = 0;  
  20.   
  21.         returnFlags |= BUFFER_NEEDS_REALLOCATION;//需要重启分配缓冲区  
  22.     } else {  
  23.         // We add 1 because that will be the frame number when this buffer  
  24.         // is queued  
  25.         mCore->mBufferAge =  
  26.                 mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;  
  27.     }  
  28.   
  29.     BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64,  
  30.             mCore->mBufferAge);  
  31.   
  32.     if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {  
  33.         BQ_LOGE("dequeueBuffer: about to return a NULL fence - "  
  34.                 "slot=%d w=%d h=%d format=%u",  
  35.                 found, buffer->width, buffer->height, buffer->format);  
  36.     }  
  37.   
  38.     eglDisplay = mSlots[found].mEglDisplay;  
  39.     eglFence = mSlots[found].mEglFence;  
  40.     *outFence = mSlots[found].mFence;  
  41.     mSlots[found].mEglFence = EGL_NO_SYNC_KHR;  
  42.     mSlots[found].mFence = Fence::NO_FENCE;  
  43.   
  44.     mCore->validateConsistencyLocked();  
  45. // Autolock scope  
  46.   
  47. if (returnFlags & BUFFER_NEEDS_REALLOCATION) {//如果需要重启分配图像缓冲区  
  48.     status_t error;  
  49.     BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);  
  50.     sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(//创建图像缓冲区  
  51.             width, height, format, usage, &error));  
  52.     if (graphicBuffer == NULL) {  
  53.         BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");  
  54.         return error;  
  55.     }  
  56.   
  57.     { // Autolock scope  
  58.         Mutex::Autolock lock(mCore->mMutex);  
  59.   
  60.         if (mCore->mIsAbandoned) {  
  61.             BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");  
  62.             return NO_INIT;  
  63.         }  
  64.   
  65.         graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);  
  66.         mSlots[*outSlot].mGraphicBuffer = graphicBuffer;  
  67.     } // Autolock scope  
  68. }  
  69. ......  

mAllocator的类型是IGraphicBufferAlloc,也是一个Binder对象,它是在BufferQueueCore的构造函数中得到的,这个时候allocator为空的,具体要从Layer的构造函数中调用BufferQueue::createBufferQueue函数是,那个时候allocator参数就为空。然后通过getComposerService来调用createGraphicBufferAlloc函数来创建这个mAllocator对象。

之前的博客分析过getComposerService返回的是和SurfaceFlinger进程的Binder对象,因此最后是到SurfaceFlinger的createGraphicBufferAlloc函数中去了(但是这里有点搞不明白明明是在一个进程中为什么要用Binder呢?)。

[cpp] view plain copy
  1. ......  
  2. if (allocator == NULL) {  
  3.     sp<ISurfaceComposer> composer(ComposerService::getComposerService());  
  4.     mAllocator = composer->createGraphicBufferAlloc();  
  5.     if (mAllocator == NULL) {  
  6.         BQ_LOGE("createGraphicBufferAlloc failed");  
  7.     }  
  8. }  
  9. ......  

下面我们来看SurfaceFlinger的createGraphicBufferAlloc函数。

[cpp] view plain copy
  1. sp<IGraphicBufferAlloc> SurfaceFlinger::createGraphicBufferAlloc()  
  2. {  
  3.     sp<GraphicBufferAlloc> gba(new GraphicBufferAlloc());  
  4.     return gba;  
  5. }  

因此最后BufferQueueProducer中的dequeueBuffer函数中调用mCore->mAllocator的createGraphicBuffer函数就是调用了GraphicBufferAlloc的createGraphicBufferAlloc函数。

[cpp] view plain copy
  1. sp<GraphicBuffer> GraphicBufferAlloc::createGraphicBuffer(uint32_t width,  
  2.         uint32_t height, PixelFormat format, uint32_t usage, status_t* error) {  
  3.     sp<GraphicBuffer> graphicBuffer(  
  4.             new GraphicBuffer(width, height, format, usage));  
  5.     status_t err = graphicBuffer->initCheck();  
  6.     *error = err;  
  7.     ......//错误处理  
  8.     return graphicBuffer;  
  9. }  

我们来看下GraphicBuffer对象的构造函数中调用了initSize函数。

[cpp] view plain copy
  1. GraphicBuffer::GraphicBuffer(uint32_t inWidth, uint32_t inHeight,  
  2.         PixelFormat inFormat, uint32_t inUsage)  
  3.     : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),  
  4.       mInitCheck(NO_ERROR), mId(getUniqueId())  
  5. {  
  6.     ......  
  7.     mInitCheck = initSize(inWidth, inHeight, inFormat, inUsage);  
  8. }  

在initSize函数中调用GraphicBufferAllocator的alloc来分配内存。

[cpp] view plain copy
  1. status_t GraphicBuffer::initSize(uint32_t inWidth, uint32_t inHeight,  
  2.         PixelFormat inFormat, uint32_t inUsage)  
  3. {  
  4.     GraphicBufferAllocator& allocator = GraphicBufferAllocator::get();  
  5.     uint32_t outStride = 0;  
  6.     status_t err = allocator.alloc(inWidth, inHeight, inFormat, inUsage,  
  7.             &handle, &outStride);  
  8.     if (err == NO_ERROR) {  
  9.         width = static_cast<int>(inWidth);  
  10.         height = static_cast<int>(inHeight);  
  11.         format = inFormat;  
  12.         usage = static_cast<int>(inUsage);  
  13.         stride = static_cast<int>(outStride);  
  14.     }  
  15.     return err;  
  16. }  
alloc又调用了成员变量mAllocDev的alloc函数。

[cpp] view plain copy
  1. status_t GraphicBufferAllocator::alloc(uint32_t width, uint32_t height,  
  2.         PixelFormat format, uint32_t usage, buffer_handle_t* handle,  
  3.         uint32_t* stride)  
  4. {  
  5.     ......  
  6.     err = mAllocDev->alloc(mAllocDev, static_cast<int>(width),  
  7.             static_cast<int>(height), format, static_cast<int>(usage), handle,  
  8.             &outStride);  

在GraphicBufferAllocator的构造函数中装载了Gralloc模块,因此mAllocDev指向了Gralloc模块。这个会在后面的博客中分析

[cpp] view plain copy
  1. GraphicBufferAllocator::GraphicBufferAllocator()  
  2.     : mAllocDev(0)  
  3. {  
  4.     hw_module_t const* module;  
  5.     int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);  
  6.     ALOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);  
  7.     if (err == 0) {  
  8.         gralloc_open(module, &mAllocDev);  
  9.     }  
  10. }  

这里调用alloc分配了一块共享的内存缓冲区,alloc函数将返回共享区的fd和缓冲区的指针。既然GraphicBuffer中的缓冲区是共享内存,我们知道使用共享内存需要传递共享内存的句柄fd。下面我们看看是如何传到客户进程的。



2.2 内存缓冲区的fd传递到客户进程

GraphicBuffer类从模板类Flattenable派生,这个派生类可以通过Parcel传递,通常派生类需要重载flatten和unflatten方法,用于对象的序列化和反序列化。

[cpp] view plain copy
  1. class GraphicBuffer  
  2.     : public ANativeObjectBase< ANativeWindowBuffer, GraphicBuffer, RefBase >,  
  3.       public Flattenable<GraphicBuffer>  

我们先来看下flatten函数,fds参数用来传递文件句柄,函数把handle中的句柄复制到fds中,因此这些句柄就能通过binder传递到目标进程中去。

[cpp] view plain copy
  1. status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {  
  2.     size_t sizeNeeded = GraphicBuffer::getFlattenedSize();  
  3.     if (size < sizeNeeded) return NO_MEMORY;  
  4.   
  5.     size_t fdCountNeeded = GraphicBuffer::getFdCount();  
  6.     if (count < fdCountNeeded) return NO_MEMORY;  
  7.   
  8.     int32_t* buf = static_cast<int32_t*>(buffer);  
  9.     buf[0] = 'GBFR';  
  10.     buf[1] = width;  
  11.     buf[2] = height;  
  12.     buf[3] = stride;  
  13.     buf[4] = format;  
  14.     buf[5] = usage;  
  15.     buf[6] = static_cast<int32_t>(mId >> 32);  
  16.     buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);  
  17.     buf[8] = static_cast<int32_t>(mGenerationNumber);  
  18.     buf[9] = 0;  
  19.     buf[10] = 0;  
  20.   
  21.     if (handle) {  
  22.         buf[9] = handle->numFds;  
  23.         buf[10] = handle->numInts;  
  24.         memcpy(fds, handle->data,//把handle中的中复制到fds中  
  25.                 static_cast<size_t>(handle->numFds) * sizeof(int));  
  26.         memcpy(&buf[11], handle->data + handle->numFds,  
  27.                 static_cast<size_t>(handle->numInts) * sizeof(int));  
  28.     }  
  29.   
  30.     buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);  
  31.     size -= sizeNeeded;  
  32.     if (handle) {  
  33.         fds += handle->numFds;  
  34.         count -= static_cast<size_t>(handle->numFds);  
  35.     }  
  36.   
  37.     return NO_ERROR;  
  38. }  
再来看unflatten函数,调用这个函数时,共享区的文件句柄已经准备好了,但是内存还没有进行映射,调用了mBufferMapper.registerBuffer函数来进行内存映射。
[cpp] view plain copy
  1. status_t GraphicBuffer::unflatten(  
  2.         void const*& buffer, size_t& size, int const*& fds, size_t& count) {  
  3.     ......  
  4.     if (handle != 0) {  
  5.         status_t err = mBufferMapper.registerBuffer(handle);  
  6.         ......  
  7.     }  
  8.   
  9.     buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);  
  10.     size -= sizeNeeded;  
  11.     fds += numFds;  
  12.     count -= numFds;  
  13.   
  14.     return NO_ERROR;  
  15. }  
我们看下GraphicBufferMapper::registerBuffer函数,调用了mAllocMod的registerBuffer函数,mAllocMod同样是指向了Gralloc模块的指针,在GraphicBufferMapper的构造函数中创建,因此实际是调用了Gralloc模块的gralloc_register_buffer函数。这个函数就是调用了mmap来进行共享内存的映射。在后续的博客我们再详细分析。
[cpp] view plain copy
  1. status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle)  
  2. {  
  3.     ATRACE_CALL();  
  4.     status_t err;  
  5.   
  6.     err = mAllocMod->registerBuffer(mAllocMod, handle);  
  7.   
  8.     ALOGW_IF(err, "registerBuffer(%p) failed %d (%s)",  
  9.             handle, err, strerror(-err));  
  10.     return err;  
  11. }  

在硬件设备支持Framebuffer缓冲区的情况下,Surface中绘制图形的缓冲区就是Framebuffer的缓冲区,绘制完成后,如果不需要进行图像合成,只需要flip操作就能完成图像的输出,中间完全不用复制的过程,很高效。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 莲藕洞里的黑膜怎么办 鞋子后面的拉链磨脚怎么办 磨砂鞋尖踢掉皮怎么办 塔吊吊装过程中遇到障碍物怎么办 高速路上突然遇到障碍物怎么办 被不明物蛰了怎么办 喝酒后开车撞路政护栏怎么办 电脑过了质保期怎么办 电闸推不上去怎么办啊? 寝室的电闸不亮了怎么办 海绵城市改造只挖不修怎么办 怀孕一个月做了胸透怎么办 怀孕了照了胸透怎么办 刚怀孕照了胸透怎么办 怀孕进了ct室怎么办 消防稳压罐气囊压力不够怎么办 备孕期间拍了ct怎么办 晚上睡不好老做梦怎么办 零线和地线短路怎么办 火花塞拧的过紧怎么办 眼睛被电弧光灼伤怎么办 电脑模拟器微信附近人屏蔽了怎么办 美的电压锅漏气怎么办 电压力锅排气阀漏气怎么办 win10系统上不了网怎么办 手机自拍杆坏了怎么办 车辆遭核生化打击时怎么办? 遭核生化打击时怎么办? 车辆行驶中遭核生化打击时怎么办? 硫酸气体熏到嗓子怎么办 硫酸味儿进嗓子了怎么办 吸入二氧化氯气体后怎么办 衣服84消毒 变色了怎么办 我的身份证被诈骗犯知道了怎么办 母婴店来客人少怎么办 键盘驱动被卸载了怎么办 广州 狗狗随地大小便怎么办 上海 狗狗随地大小便怎么办 服装店人流量少生意差怎么办 2018年服装店生意特别差怎么办 打印机拍的照片打出黑怎么办