Android Display Graphics #BufferQueue与Gralloc

 如果图片链接失败,请扫码查看文章详情或点击系列文章汇总链接。

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继承自BufferItemConsumerBufferItemConsumer继承自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会触发缓冲区的事务提交:


如下的调用栈可以看出调用关系:

  1. Producer的绑定

前面文章《从Activity看surface的创建》中介绍了Surface的创建过程,创建BBQ的流程,在ViewRootImpl.getOrCreateBLASTSurface方法中,创建完BBQ,紧接着会创建Surface对象,直接看Native 对象的构造函数:

Surface的构造函数会传入生产者模型 GraphicBufferProducer,这使Surface对象拥有了操作缓冲区的能力。构造函数中Surface也提供了一系列hook为首的函数,连接到ANativeWindow的函数指针,为的是给EGL模块提供对缓冲区操作的入口。而hook函数会直接调用内部的本地函数。

APP绘制不需要通过hook函数来中转,当上层通过Surface.lockCanvas方法获取画布时会直接调用本地函数函数 Surface::dequeueBuffer

  1. 数据流

下图是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不能访问内容SlotConsumer持有通过releaseBuffer或detachBuffer转换为FREE。detached buffer 可以通过attachBuffer()进入ACQUIRED状态。

SHARED:表示buffer正以共享缓冲区模式使用。它可以是除FREE外,同时处于其他状态的任何组合。 也可以多次DequeuedQueuedAcquired

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() 的调用流程是:

  1. 关系图与类图

 如果图片链接失败,请扫码查看文章详情或点击系列文章汇总链接。

Android Display Graphics系列文章-汇总

系列文章请扫关注公众号!

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dwyane05

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值