App的启动过程(7)BufferQueue的申请分配

时间:2021-06-02 17:39:19

接下来看下BufferQueue的申请分配,就是经典的生产者-消费者模型。

1BufferQueueCore

可以认为BufferQueueCore是一个服务中心,生产者、消费者都要通过它来管理buffer。里面有一个重要的成员数组:BufferQueueDefs::SlotsType mSlots;这个BufferSlot中有一个成员变量:sp<GraphicBuffer>mGraphicBuffer;记录这个slot所涉及的缓冲区;另一个变量BufferState mBufferState;用于跟踪这个缓冲区的状态。缓冲区的状态有以下几种:

FREEbuffer当前可用,可以被生产者dequeued,此时ownerBufferQueueCore,当dequeuebuffer调用时,状态可以转为dequeued

DEQUEUEDbuffer已经被dequeued,还没被queuecanceld,此时ownerproducer

QUEUEDbuffer已经被生产者填充,并被queued,此时的ownerbufferQueueCore

ACQUIREDbuffer已经被消费者获得,此时的ownerconsumer

SHARED:可以认为是除FREE外的其他状态的联合体,可以被多次dequeuedqueuedacquired

一块buffer的生命周期依次是:FREEà DEQUEUEDà QUEUEDà ACQUIREDà FREE

2BufferQueueProducer

生产者就是填充buffer数据的人,通常是应用程序不断刷新UI,将数据写到buffer中。档producer需要一块buffer时,会先向BufferQuueuCore发起dequeue申请,然后对申请到的buffer操作,档数据写完后,调用queue接口,把buffer归还到bufferqueue的队列中。

3BufferQueueConsumer

当一块buffer就绪后,消费者就开始工作了,consumerbuffer的处理是被动的,它必须要等到一块buffer填充好才能工作,那consumer怎么知道一块buffer已经填充好了?这里提供了另外一个类ConsumerListener

class ConsumerListener : public virtualRefBase {

//两个纯虚函数,当一块buffer可以被消费时,onFrameAvailable会被调用,当BufferQueue通知consumer它已经释放其mSlot中的一个或多个GraphicBuffer的引用时,会调用onBuffersReleased

         virtualvoid onFrameAvailable(const BufferItem& item) = 0;

         virtualvoid onBuffersReleased() = 0;

}

什么时候注册的这个ConsumerListener监听呢?这样看具体的使用consumer的代码场景。下面以SurfaceFlinger为例,它是由framebufferSurface间接操作bufferconsumer对象,

void SurfaceFlinger::init() @SurfaceFlinger_hwc1.cpp{

//这里通过BufferQueue::createBufferQueue,创建了BufferQueueCore实例,BufferQueueProducer实例,BufferQueueConsumer实例,并且BufferQueueCore作为后两者的参数,这样producerconsumer就跟服务中心建立了联系。

         sp<IGraphicBufferConsumer>consumer;

         BufferQueue::createBufferQueue(&producer,&consumer, new GraphicBufferAlloc());

//接着把consumer作为参数,创建FramebufferSurface实例

         sp<FramebufferSurface>fbs = new FramebufferSurface(*mHwc, i, consumer);

}

看继承关系:FramebufferSurface继承了ConsumerBase

class FramebufferSurface : publicConsumerBase,…

所以在调用FramebufferSurface的构造函数时,也会调用父类ConsumerBase的构造函数,并且把consumer作为参数也传给了父类ConsumerBase

FramebufferSurface::FramebufferSurface(HWComposer&hwc, int disp,

         constsp<IGraphicBufferConsumer>& consumer) :

         ConsumerBase(consumer),…

那么接着看ConsumerBase(consumer)的处理:

/* ConsumerBase.cpp */

ConsumerBase::ConsumerBase(constsp<IGraphicBufferConsumer>& bufferQueue,

boolcontrolledByApp) :

mConsumer(bufferQueue){

//创建消费者监听类对象

         wp<ConsumerListener>listener = static_cast<ConsumerListener*>(this);

         sp<IConsumerListener>proxy = new BufferQueue::ProxyConsumerListener(listener);

//connect连接,经过层层调用,把listener监听注册到bufferQueuecore中,

这样bufferQueuecore就可以利用这个监听通知到具体的消费者

status_t err =mConsumer->consumerConnect(proxy, controlledByApp);

(consumerConnect@IGraphicBufferConsumer.cppà

consumerConnect@BufferQueueConsumer.hà

BufferQueueConsumer::connect@BufferQueueConsumer.cppà

mCore->mConsumerListener =consumerListener;//这里把consumerListener注册到服务者类bufferQueuecore)   

}

这样就清楚了buffer的生产者,消费者,管理者之间的关系。

接着真正开始看下bufferqueuecore中缓冲区的分配:

前面看到,bufferqueuecoremSlots数组管理缓冲区,最大容量是64,这个mSlots一开始静态分配了64bufferslot大小的空间,但是其中的数据缓冲区不是一次性分配的,不然就太浪费空间了,所以缓冲区的空间分配是动态的,具体就是producerdequeuebuffer时,如果没有获取到可用的缓冲区,那就要重新分配空间了。

还接着前面分析的Surface,这个应用程序段的本地窗口要要向上层提供绘制图像的画板,即GraphicBuffer,那么它是怎么申请buffer的?这个过程很多的跨进程通信,

Surface::dequeueBuffer@Surface.cpp{

//调用IGraphicBufferProducer.cppdequeueBuffer方法,这个类是Binder的客户端,对应服务端的实现是BufferQueueProducer.cpp

         status_tresult = mGraphicBufferProducer->dequeueBuffer(

                   &buf,&fence,reqWidth, reqHeight, reqFormat, reqUsage);

// BUFFER_NEEDS_REALLOCATION这个标记是native层传上来的,如果buffer需要重新分配空间,要调用requestBuffer,获取新buffer的地址,

         if((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0){

                  result = mGraphicBufferProducer->requestBuffer(buf,&gbuf);}

}

详细看下dequeuebuffer的调用流程:

virtual status_t dequeueBuffer()@IGraphicBufferProducer.cpp{

//把请求传到服务端

         status_tresult = remote()->transact(DEQUEUE_BUFFER, data, &reply);

}

status_t BnGraphicBufferProducer::onTransact()@IGraphicBufferProducer.cpp{

         caseDEQUEUE_BUFFER: {

//转到服务端实现类中的dequeueBuffer

                  int result = dequeueBuffer(&buf,&fence, width, height, format,  }

}

从继承关系可以看出BufferQueueProducerIGraphicBufferProducer对应的server段:

class BufferQueueProducer : publicBnGraphicBufferProducer,…

这个函数很长,核心是查找一个符合要求的slot,对这个bufferslot做初始化,根据判断结果决定是否要重新分配空间

status_tBufferQueueProducer::dequeueBuffer( )@BufferQueueProducer.cpp{

//这个函数是获取一个slot,具体是getFreeBufferLocked或者getFreeSlotLocked,返回这个bufferslotmSlots数组中的序号

         BufferQueueProducer::waitForFreeSlotThenRelock(){

int slot = getFreeBufferLocked();

*found = getFreeSlotLocked(); }

//判断这个GraphicBuffer是否需要重新分配空间,判断条件就是buffer为空,因为它初始值是null,第一次使用它需要分配空间,如果不为null,但是bufferwidthheightformatusage属性跟要求的不一致,也要重新分配。

         constsp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);

         if((buffer == NULL) || buffer->needsReallocation(width, height, format,usage)){

//除了对bufferslot对象初始化之外,还要设置BUFFER_NEEDS_REALLOCATION这个标记,客户端surface在发现这个flag后,会调用requestBuffer获取新buffer的地址,这里客户端和buffer管理所处的服务端是分在两个进程的,这个buffer地址实际是匿名共享内存地址,这也是binder通信中的原理。

                   mSlots[found].mAcquireCalled= false;

                   mSlots[found].mGraphicBuffer= NULL;

                   mSlots[found].mRequestBufferCalled= false;         …

                   returnFlags|= BUFFER_NEEDS_REALLOCATION;

}

//需要重新分配,调用BufferQueueCore中的GraphicBufferAlloc中函数createGraphicBuffer,生成一个newGraphicBuffer(width, height, format, usage, std::move(requestorName)));实例。

         if(returnFlags & BUFFER_NEEDS_REALLOCATION) {

         sp<GraphicBuffer>graphicBuffer(mCore->mAllocator->createGraphicBuffer(

                   width,height, format, usage,

{mConsumerName.string(), mConsumerName.size()}, &error));

}

}

到这里,客户端surface就完成了对buffer的申请。

 

前面分析performDrawDrawSoftware时,在应用程序绘图结束,会将buffer解锁,会调用surface.unlockCanvasAndPost(canvas);提交给系统进行渲染,这个函数最终调用到surface.cpp中的函数:unlockAndPost

status_t Surface::unlockAndPost()@Surface.cpp{

//主要想看把这个buffer入队时,怎么样通知consumer来消费这个buffer的。

         err= queueBuffer(mLockedBuffer.get(), fd);à

//接着调用到IGraphicBufferProducer中的queueBuffer方法,这里是通过binder实现的跨进程通信,IGraphicBufferProducer对应服务端的实现是BufferQueueProducer.cpp

         status_terr = mGraphicBufferProducer->queueBuffer(i, input, &output);

}

status_t BufferQueueProducer::queueBuffer()@BufferQueueProducer.cpp{

//这里定义一个IConsumerListener,后面会执行具体的监听实例

         sp<IConsumerListener>frameAvailableListener;

//把通过BufferQueueCore注册的IConsumerListener类的监听赋给frameAvailableListener

         frameAvailableListener= mCore->mConsumerListener;

//调用onFrameAvailable通知消费者有已经渲染好的,可用的buffer

         frameAvailableListener->onFrameAvailable(item);

}

会监听FrameAvailable的有FramebufferSurface,还有layer.cpp

void FramebufferSurface::onFrameAvailable()@FramebufferSurface.cpp{

//获取可用buffer

         sp<GraphicBuffer>buf;

         sp<Fence>acquireFence;

         status_terr = nextBuffer(buf, acquireFence);

//nextbuffer时,会调用acquireBuffer把这个buffer的状态置为acquire

         status_terr = acquireBufferLocked(&item, 0);

//把这个buffer送到framebuffer中,也就是fb设备中,准备合成,送到屏幕显示

         err= mHwc.fbPost(mDisplayType, acquireFence, buf);

}

surfaceflinger这个consumer为例,SurfaceFlingerConsumer继承了FrameAvailableListener,而layer继承了SurfaceFlingerConsumer

void Layer::onFrameAvailable(constBufferItem& item) {

 

}

 

总结下surfacesurfaceflingerbufferQueue,应用程序之间的联系。

一个应用程序是可以创建多个layer的,一个layer对应一个BufferQueue,一个BufferQueue中有多个GraphicBuffer,就是那个mSlots数组;可以认为surface在服务端对应的就是layer,一个客户端程序拥有的surface数量可能不止一个,通常一个activity下的UI布局共用一个surface进行绘图,一个activity对应一个viewtree,一个viewrootimpl,还记得app申请一个surface的起点就是viewrootimplesetview方法,一个app是可以有多个activity的。还有像surfaceview这类UI控件是独占一个surface进行绘制的,比如图库,camera,视频播放器都有surfaceview这类控件,所以他们的surface应该不止一个。

1)、ISurfaceComposerClient这个类是应用程序跟surfaceflinger间的通道,具体被封装在SurfaceComposerClient类中,通过surfaceflingercreateConnection方法获取,对应服务端的实现是Client.cpp

2)、IGraphicBufferProducer.cpp,这个类实例有应用程序创建Surface时得到,对于纯C++的应用程序来说,是通过sp<SurfaceControl>SurfaceComposerClient::createSurface()这个函数,这个调用走到surfaceflinger,还会创建一个layer,这个layer类似于windowmanagerservice中的windowState这个窗口,都是代表一个画面,同时会有一个控制这个layerhandle保存在应用端的surfacecontrol中;那么对于java层的应用程序,它的窗口或者说layer的层级是由wms中的windowlist来表示的,

3)、Surface、从逻辑看,他是上面说的layer的使用者。从继承关系看,他是一个本地窗口,内部有IGraphicBufferProducer,当EGL想利用这个本地窗口surface完成一个功能时,surface实际上利用handleIGraphicBufferProducer来获取远程服务端的服务,完成egl的请求。

值得一提的是c++层的应用程序创建surface是通过SurfaceComposerClient这个实例的方法来实现的;而java层的应用程序是通过SurfaceControl.cpp中的getSurface()返回了一个surface实例,并没有跟SurfaceComposerClient产生联系,所以真正跟Surfaceflinger间有联系的就是surface中的IGraphicBufferProducer,也就是它与BufferQueue有联系。