如果图片链接失败,请扫码查看文章详情或点击系列文章汇总链接。
Android Display Graphics系列文章-汇总
系列文章请扫关注公众号!
系列文章请扫关注公众号!
1、BufferQueue
下面这个图片是Google的官方示例,描述了BufferQueue生产者消费者模型。
Android S 版本引入了BLAST Buffer Queue,BLAST主要解决:
1、应用程序在缓冲区的提交中灵活性不足;2、窗口与应用绘制之间无法做到同步,多进程数据无法做到同步;3、简化 SF 中的模型。
Android S 版本之前
- Producer一般是app,Consumer一般指SurfaceFlinger。
- 应用绘制缓冲区仅能通过 BufferQueue IGBP(IGraphicBufferProducer) 提交;
- 应用窗口Geometry的改变仅能通过事务(Transaction)提交;
- 通过合并事务(Transaction.merge())或延迟事务来更改应用窗口间的Geometry;
- 多进程缓冲区之间无法做到同步。
Android S 或更高版本
- BufferQueue 完全存在于 App 进程,dequeue,queue,acquire,release Buffer 的操作均由 App 进行。
- 应用绘制缓冲区可以通过事务Transaction.setBuffer()进行提交;
- 应用窗口Geometry的改变可以通过BlastBufferQueue进行提交;
- 应用绘制的缓冲区和应用窗口Geometry可以进行同步;
- 多应用绘制的缓冲区之间可以进行同步。
2、BlustBufferQueue初始化
在ViewRootImpl的performTraversals方法中,如果App层使用BLASTBufferQueue,会在relayoutWindow()时,构造new BLASTBufferQueue对象,进而一步步调用到BLASTBufferQueue.cpp的BLASTBufferQueue构造函数。
CreateBufferQueue()函数中构造了BufferQueueCore、IGraphicBufferProducer、BufferQueueConsumer。
从ViewrootImpl到BlastBufferQueue的调用流程大致如下:
3、Consumer的绑定
BLASTBufferQueue::BLASTBufferQueue()构造函数中,在
createBufferQueue(&mProducer, &mConsumer)后,又初始化图形缓冲区消费者
mBufferItemConsumer = new BLASTBufferItemConsumer();
BLASTBufferItemConsumer继承自BufferItemConsumer,BufferItemConsumer继承自ConsumerBase.
创建BLASTBufferItemConsumer
时,在ConsumerBase
的构造函数中实现了如下功能:
1)将ConsumerBase转换为ConsumerListener。
2)将ConsumerListener封装为ProxyConsumerListener。
3)向消费模型注册回调。
IGraphicBufferConsumer的cosumerConnect方法是一个跨进程调。最终会调用BufferQueueConsumer的connect方法。
这一步也就让 BLASTBufferItemConsumer 建立了对Buffer状态的监听。
调用流程如下:
4、设置图形缓冲区监听器
BLASTBufferItemConsumer 具有监听Buffer所有状态的能力,BBQ对Buffer特定状态的监听离不开 BLASTBufferItemConsumer ,因此,BBQ 继承了两个抽象类 ConsumerBase 与 BufferItemConsumer,分别针对 Buffer 消费状态与生产状态进行监听。
BBQ初始化完成,消费者模型建立完成,由于BBQ动态监听缓冲区的状态,如果有可消费的缓冲区,BBQ会触发缓冲区的事务提交:
如下的调用栈可以看出调用关系:
- Producer的绑定
前面文章《从Activity看surface的创建》中介绍了Surface的创建过程,创建BBQ的流程,在ViewRootImpl.getOrCreateBLASTSurface方法中,创建完BBQ,紧接着会创建Surface对象,直接看Native 对象的构造函数:
Surface的构造函数会传入生产者模型 GraphicBufferProducer,这使Surface对象拥有了操作缓冲区的能力。构造函数中Surface也提供了一系列hook为首的函数,连接到ANativeWindow的函数指针,为的是给EGL模块提供对缓冲区操作的入口。而hook函数会直接调用内部的本地函数。
APP绘制不需要通过hook函数来中转,当上层通过Surface.lockCanvas方法获取画布时会直接调用本地函数函数 Surface::dequeueBuffer。
- 数据流
下图是BufferQueue机制的数据流,根据代码分析下各自调用流程:
Buffer有5中状态,定义如下:
FREE:表示Buffer可以被producer在dequeue时获取。它的slot由BufferQueue持有。当调用dequeueBuffer时,buffer转换为DEQUEUED。
DEQUEUED:表示buffer已被producer从Bufferqueue中取出,但还未QUEUED或CANCEL。当这个Buffer的释放栅栏发出信号(release fence is signaled)时,producer就可以修改Buffer的内容。Slot由producer持有。它通过queueBuffer或attachBuffer可以转换到QUEUED,或通过cancelBuffer或detachBuffer转换成FREE。
QUEUED:表示缓冲区已被Producer填充,并入队供Consumer使用。Buffer内容可以在有限的时间内继续被修改,但在相关的fence 被signaled前不能访问内容。该Slot由BufferQueue持有。它通过acquireBuffer可以转换到ACQUIRED,如果另一个缓冲区以异步模式排队则转换成FREE。
ACQUIRED:表示Buffer已被Consumer获取。与QUEUED一样,在获取栅栏(acquire fence)发出信号(is signaled)之前,Consumer不能访问内容。 Slot由Consumer持有。通过releaseBuffer或detachBuffer转换为FREE。detached buffer 可以通过attachBuffer()进入ACQUIRED状态。
SHARED:表示buffer正以共享缓冲区模式使用。它可以是除FREE外,同时处于其他状态的任何组合。 也可以多次Dequeued、Queued或Acquired 。
6.1 DequeueBuffer
Surface创建时,传入参 GraphicBufferProducer,Surface可以操作Buffer。构造函数中Surface提供了一系列hook为首的函数,连接到ANativeWindow的函数指针,给EGL模块提供对Buffer操作的接口。而hook函数会直接调用内部的本地函数,以hook_dequeueBuffer 为例:
hook_dequeueBuffer调用生产者mGraphicBufferProducer的dequeueBuffer
BpGraphicBufferProducer dequeueBuffer
调用mGraphicBufferProducer(IGraphicBufferProducer)的dequeueBuffer方法,IGraphicBufferProducer是一个接口,由BpGraphicBufferProducer实现:
transact发送DEQUEUE_BUFFER消息,发送的消息在BnGraphicBufferProducer的onTransact方法中处理:
BufferQueueProducer dequeueBuffer
BnGraphicBufferProducer继承BufferQueueProducer,调用BnGraphicBufferProducer->dequeueBuffer就会调用
BufferQueueProducer->dequeueBuffer方法:
当第一次调用,flag包含BUFFER_NEEDS_REALLOCATION时。创建一个new GraphicBuffer()
GraphicBuffer的构造方法如下:
调用函数 initWithSize,又调用GraphicBufferAllocator申请一块内存。
调用allocator(GraphicBufferAllocator)的allocate方法,分配一块制定宽高的 GraphicBuffer:
一般情况下系统有多个版本的allocate实现,一般是最高版本生效。
Android13上是Gralloc4:
dequeueBuffer函数执行完毕之后,Surface一端就拿到了可用的GraphicBuffer。
6.2 QueueBuffer
这个调用比较简单,就是将Buffer入队。在BufferQueueProducer::queueBuffer()中调用。
调用流程图为:
6.3 onFrameAvailable和AcquireBuffer
当 queueBuffer() 调用 onFrameAvailable() 的时候,实际上调用的就是 BufferQueue::ProxyConsumerListener::onFrameAvailable():
这个 listener 就是 BufferQueue::ProxyConsumerListener 构造函数传进来的 ConsumerBase,所以转而调到 ConsumerBase::onFrameAvailable():
这里又调用到了mFrameAvailableListener ,它是什么东东呢?在ConsumerBase中声明和赋值。
ConsumerBase::setFrameAvailableListener()函数的赋值什么时候调用的呢?在BLASTBufferQueue() 的构造函数中,调用了 mBufferItemConsumer->setFrameAvailableListener(this);这就把 BLASTBufferQueue 给设置为 ConsumerBase 的 mFrameAvailableListener。
也就是说queueBuffer() 的 onFrameAvailable() 最终是调用到 BLASTBufferQueue::onFrameAvailable(),在 BLASTBufferQueue 里完成对 BufferItem 的 acquire 操作。
acquireBuffer() 的调用流程是:
- 关系图与类图
如果图片链接失败,请扫码查看文章详情或点击系列文章汇总链接。
Android Display Graphics系列文章-汇总
系列文章请扫关注公众号!