1.背景
对于业务开发来说,你接触不到BufferQueue,甚至不知道BufferQueue是什么。对于系统来说,缓冲队列是传输数据的一个非常重要的组件。安卓显示系统依靠BufferQueue。只要内容显示在“屏幕”上(这里指的是抽象屏幕,有时也可以包括编码器),就必须使用BufferQueue。可以说BufferQueue在显示器/播放器的理解中无处不在。即使直接调用Opengl ES进行绘制,底层仍然需要BufferQueue在屏幕上显示。
了解BufferQueue不仅可以增强对Android系统的理解,还可以了解/排查相关问题,比如Mediacodec调用出列缓冲时为什么总是返回-1?为什么普通View的绘制方法可以直接绘制内容,绘制后的SurfaceView需要unlockCanvasAndPost?
注意:本文分析的代码来自于Android6.0.1。
2.缓冲队列的内部操作模式
BufferQueue是Android显示系统的核心,其设计理念是生产者-消费者模型。只要将数据填充到BufferQueue中,就被认为是生产者,只要从BufferQueue中获取数据,就被认为是消费者。有时在不同的场景中,同一个类可能既是生产者又是消费者。比如SurfaceFlinger,在合成和显示UI内容时,UI元素作为生产者产生内容,SurfaceFlinger作为消费者消费这些内容。在截屏时,SurfaceFlinger作为生产者,将当前复合显示的UI内容填充到另一个BufferQueue中。此时,屏幕捕获应用程序作为消费者,从缓冲队列中获取数据并生成屏幕截图。
以下是安卓官网介绍:
以下是使用缓冲队列的常见步骤:
初始化一个BufferQueue图形数据的生产者通过BufferQueue申请一块GraphicBuffer,对应图中的dequeueBuffer方法申请到GraphicBuffer后,获取GraphicBuffer,通过函数requestBuffer获取获取到GraphicBuffer后,通过各种形式往GraphicBuffer中填充图形数据后,然后将GraphicBuffer入队到BufferQueue中,对应上图中的queueBuffer方法在新的GraphicBuffer入队BufferQueue时,BufferQueue会通过回调通知图形数据的消费者,有新的图形数据被生产出来了然后消费者从BufferQueue中出队一个GraphicBuffer,对应图中的acquireBuffer方法待消费者消费完图形数据后,将空的GraphicBuffer还给BufferQueue以便重复利用,此时对应上图中的releaseBuffer方法此时BufferQueue再通过回调通知图形数据的生产者有空的GraphicBuffer了,图形数据的生产者又可以从BufferQueue中获取一个空的GraphicBuffer来填充数据一直循环2-8步骤,这样就有条不紊的完成了图形数据的生产-消费当然,图形数据的生产者可以在不等待BufferQueue回调的情况下再现数据,但是继续生产数据,然后排队到BufferQueue,直到BufferQueue满为止。图形数据的消费者也可以尝试每次都从BufferQueue获取数据,而不需要等待BufferQueue的回调通知,但是这样效率不高,需要不断的对BufferQueue进行轮换训练(因为BufferQueue有同步阻塞和异步阻塞两种类型,在异步阻塞机制下获取数据失败,直到有数据才会阻塞线程,直接返回-1)。
同时,使用BufferQueue的生产者和消费者往往处于不同的流程中。BufferQueue使用共享内存和Binder在不同进程间传递数据,减少了数据复制,提高了效率。
与缓冲队列相关的几个类是:
BufferBufferCore:BufferQueue的实际实现BufferSlot:用来存储GraphicBufferBufferState:表示GraphicBuffer的状态IGraphicBufferProducer:BufferQueue的生产者接口,实现类是BufferQueueProducerIGraphicBufferConsumer:BufferQueue的消费者接口,实现类是BufferQueueConsumerGraphicBuffer:表示一个Buffer,可以填充图像数据ANativeWindow_Buffer:GraphicBuffer的父类ConsumerBase:实现了ConsumerListener接口,在数据入队列时会被调用到,用来通知消费者缓冲区用于存储图形缓冲区,数组用于存储一系列缓冲区插槽。数组的默认大小是64。
GraphicBuffer使用BufferState来表示它的状态,它有以下几种状态:
FREE:表示该Buffer没有被生产者-消费者所使用,该Buffer的所有权属于BufferQueueDEQUEUED:表示该Buffer被生产者获取了,该Buffer的所有权属于生产者QUEUED:表示该Buffer被生产者填充了数据,并且入队到BufferQueue了,该Buffer的所有权属于BufferQueueACQUIRED:表示该Buffer被消费者获取了,该Buffer的所有权属于消费者为什么需要这些状态?假设不需要这些状态,简单的缓冲队列实现如下:
BufferQueue { vector & ltGraphicBuffer & gt插槽;void push(GraphicBuffer槽){ slots.push(槽);} GraphicBuffer pull(){ return slots . pull();}}
生产者产生数据后,通过调用BufferQueue的push函数将数据插入到向量中。消费者调用缓冲队列的拉函数来排队缓冲数据。
上面实现的问题是生产者每次都需要自己创建GraphicBuffer,但是GraphicBuffer是在消费者每次消费完数据之后才发布的,GraphicBuffer是不回收的。在Android中,由于BufferQueue的生产者-消费者往往处于不同的进程中,所以需要通过GraphicBuffer中的共享内存来连接生产者-消费者进程。每次创建GraphicBuffer,都意味着需要创建共享内存,效率很低。
在BufferQueue中使用BufferState来表示GraphicBuffer的状态,解决了这个问题。每个GraphicBuffer都有它的当前状态。通过维护图形缓冲区的状态,可以完成图形缓冲区的重用。
由于BufferQueue的内部实现是BufferQueueCore,所以在下面的例子中使用BufferQueueue core来代替bufferqueue。首先介绍BufferQueueCore中对应的数据结构,然后介绍Buffer Queue的状态反转过程和生产-消费过程。
下面是Buffer的入队/出列操作和BufferState的状态反转,这里只介绍异步阻塞模式。
2.1 BufferQueueCore内部数据结构
核心数据结构如下:
Bufferqueuedefs:: slots类型ms lots:存储在数组中的插槽。数组的默认大小是Bufferqueuedefs::num _ buffer _ slots,具体是64,代表所有的槽TD:: set
BufferQueueCore初始化时,由于此时队列中没有数据,根据上面的介绍,mFreeSlots此时应该包含所有Slot,元素大小与mSlots相同。初始化代码如下:
for(int slot = 0;slot <。BufferQueueDefs::NUM _ BUFFER _ SLOTS;++slot) { mFreeSlots.insert(插槽);}
2.2生产者出列缓冲器
当制作人可以制作图形数据时,首先从BufferQueue申请一个GraphicBuffer。调用函数是BufferQueueProducer . queuebuffer,如果当前buffer队列中有GraphicBuffer可用,则返回其索引,如果不存在,则返回-1,代码在bufferqueueProductor中。流程如下:
status _ t BufferQueueProducer::出列缓冲区(int * outSlot,sp & lt安卓:围栏。* out fence,bool async,uint32 _ t width,uint32 _ t height,pixel format format,uint32 _ t usage) {//1。找到一个可用的插槽,这意味着缓冲区状态为空闲状态=等待缓存队列(“出列缓冲区”,异步,&;找到,& ampreturn flags);if (status!= NO_ERROR) {返回状态;}
//2.找到可用的插槽,并将缓冲区状态设置为出列。由于步骤1中找到的插槽状态为“空闲”,因此该步骤完成了从“空闲”到“出列”的状态切换*出列=找到;ATRACE_BUFFER_INDEX(已找到);attachedByConsumer = MSlots[已找到]。mAttachedByConsumermSlots[已找到]。mBufferState = BufferSlot::出列;
//3.如果找到的槽需要申请GraphicBuffer,则申请GraphicBuffer。这里采用了惰性加载机制。如果没有应用内存,则应用的内存存储在生成器中,以处理IF(Return flags &:BUFFER _ NEEDS _ REDISABLE){ status _ t error;sp<。GraphicBuffer & gt图形缓冲(Mcore->;mAllocator->;createGraphicBuffer(宽度、高度、格式、用法和amp错误));graphic buffer->;setGenerationNumber(Mcore->;mggenerationnumber);mSlots[* outlot]。mgraphiccbuffer = Graphicbbuffer;}}
关键是找到可用的插槽。等待重新锁定的过程如下:
status _ t bufferqueueproducer::waitforfreslothenrellock(const char * caller,bool async,int * found,status _ t * return flags) const {//1 .缓冲区太多了吗?mqueue . size()>;static _ cast & ltsize_t>。(maxBufferCount);if(ToomyBuffers){ } else {
// 2.首先找出mFreeBuffers是否可用。从2.1的介绍来看,mFreeBuffers中的元素与GraphicBuffer相关联。mCore->;mfreebuffers . empty()){ auto slot = Mcore-& gt;mfreebuffers . begin();*找到= *插槽;mCore->;mFreeBuffers.erase(插槽);} else if(Mcore->;锦葵分配&。& amp!mCore->;mFreeSlots.empty()) {
// 3.了解mFreeSlots是否可用。从2.1可以看出,这个列表会在初始化时填写,所以第一次调用不会是空。同时,这个列表中的元素需要和GraphicBuffer关联才能直接使用,关联过程是通过外层函数实现的,即auto slot = MC ore-->:mfreeslots . begin();//如果(* slot & ltmaxBufferCount){ *找到= *插槽;mCore->;mFreeSlots.erase(插槽);} } }重试=(*找到= = BufferqueueCore::INVALID _ BUFFER _ SLOT)| | ToomyBuffers;//4.如果找不到可用的插槽或缓冲区太多(在同步阻塞模式下),您可能需要等待If(重试){ if(mcore->:MDequeueueBufferCannotblock & amp;& amp(acquiredCount & lt= Mcore->;mmaxaquiredbuffercount){ return WILD _ BLOCK;} Mcore->;等待(Mcore->;mMutex);} }}waitForFreeSlotThenRelock函数会尝试查找一个可用的槽,并且可用的槽状态必须是FREE(因为是从两个FREE状态的列表中获取的),然后出列缓冲区会将状态更改为出列,即状态反转。
有两个可用的插槽由waitForFreeSlotThenRelock返回:
从mFreeBuffers中获取到的,mFreeBuffers中的元素关联了GraphicBuffer,直接可用从mFreeSlots中获取到的,没有关联上GraphicBuffer,因此需要申请GraphicBuffer并和Slot关联上,通过createGraphicBuffer申请一个GraphicBuffer,然后赋值给Slot的mGraphicBuffer完成关联摘要出列缓冲区:尝试找到一个槽,完成槽和图形缓冲区之间的关联(如果需要),然后将槽的状态从空闲转为出列。返回BufferQueueCore中mSlots槽对应的索引。
2.3生产者请求缓冲区
出列缓冲区函数获取可用槽的索引,然后通过请求缓冲区获取相应的图形缓冲区。流程如下:
status _ t BufferqueueProductor::RequestBuffer(int slot,sp & ltGraphicBuffer & gt* buf) {// 1。判断插槽参数是否合法如果(插槽
这一步不是必须的。业务层可以通过Slot的索引直接获取对应的GraphicBuffer。
2.4生产者队列缓冲区
出列缓冲区获得一个槽后,图像数据可以在该槽对应的GraphicBuffer上产生,可以是View的主线程绘制过程,SurfaceView的子线程绘制过程,甚至是MediaCodec的解码过程。
在填写完图像数据之后,你需要将Slot排队到BufferQueueCore中(数据写入后,可以传输到生产者-消费者队列,供消费者消费),并调用队列中的queueBuffer函数。排队缓冲过程如下:
status _ t BufferqueueProductor::queueBuffer(int slot,const QueueBufferInput & ampInput,queuebuffersoutput * output) {//1。首先,如果(插槽
//2.将缓冲区状态变为排队,这一步完成了从出列到排队的过程。mSlots[slot]。mFence =围栏;mSlots[slot]。mBufferState = BufferSlot::QUEUED;++ Mcore->;mFrameCountermSlots[slot]。mfrma number = Mcore->;mFrameCounter
//3.加入MQUEUEIF(mCore-->;mqueue . empty()){ Mcore-& gt;mqueue . push _ back(item);frameAvailableListener = mCore->;mConsumerListener}
// 4.如果(frameAvailableListener!=空){ frameAvailableListener->;onFrameAvailable(项);} else if (frameReplacedListener!=空){ FrameReplacedListener->;onFrameReplaced(项);}}
从上面的评论可以看出,queueBuffer的主要步骤如下:
将Buffer状态扭转成QUEUED,此步完成了Buffer的状态由DEQUEUED到QUEUED的过程将Buffer入队到BufferQueueCore的mQueue队列中回调frameAvailableListener,告知消费者有数据入队,可以来消费数据了,frameAvailableListener是消费者注册的回调Summary queueBuffer:将插槽的状态转换为排队,将其添加到mQueue,最后通知消费者有数据要加入队列
2.5消费者获得缓冲
当使用者接收到onFrameAvailable回调或主动想要消费数据时,调用acquireBuffer尝试从BufferQueueCore获取数据进行消费。消费者的代码在BufferQueueConsumer中,acquireBuffer流程如下:
status _ t bufferqueueconsumer::acquire buffer(buffer item * outff fer,nsecs _ t expected present,uint64 _ t maxframenumber) {//1。如果队列为空,则直接返回if(mcore-->;mqueue . empty()){ return NO _ BUFFER _ AVAILABLE;}
//2.取出mQueue队列的第一个元素,去掉bufferqueuecore::FIFO::iterator front(mcore->:mQueue . begin());int slot = front->;mSlot* expowffer = * front;mCore->;mQueue.erase(前);
//3.处理预期的现状。在这种情况下,几个时隙的“显示”时间可能比预期的少。在这种情况下,这些插槽已经“过时”,代码相对较长,这被忽略了。//4.更新插槽的状态是在以下情况下获取的(mcore-->;静止跟踪(前)){ MSlots[槽]。mAcquireCalled = truemSlots[slot]。mNeedsCleanupOnRelease = falsemSlots[slot]。mBufferState = BufferSlot::ACQUIRED;mSlots[slot]。mFence = Fence::NO _ Fence;}
//5.如果在步骤3中有一个直接releaseBuffer的过程,回调生产者,一些数据被消耗if (listener!= NULL){ for(int I = 0;i <。numDroppedBuffers++ I){ listener->;onbufferrelized();} }}
从上面的评论可以看出,acquireBuffer的主要步骤如下:
从mQueue队列中取出并移除一个元素改变Slot对应的状态为ACQUIRED如果有丢帧逻辑,回调告知生产者有数据被消费,生产者可以准备生产数据了Summary acquireBuffer:将Slot的状态变为ACQUIRED,从mQueue中移除,最后通知生产者有数据出列。
2.6消费者释放缓冲
消费者拿到槽后,就开始消费数据(SurfaceFlinger的UI合成等典型消费)。消费之后,他们需要通知BufferQueueCore,这个槽已经被消费者消费了,可以为生产者复制数据。释放缓冲区的过程如下:
status _ t BufferQueueConsumer::release buffer(int slot,uint64_t frameNumber,const sp & lt栅栏>。& amp释放围栏,egl显示egl显示,eglsynckhr egl围栏){//1。如果(插槽
//2.容错处理:如果要处理的Slot存在于mQueue中,说明这个Slot的来源是非法的,不是从2.5的acquireBuffer获取的Slot。BufferQueueCore:: FIFO::迭代器Current(mCore-->:mqueue . begin());while(当前!= Mcore->;mqueue . end()){ if(current->;mSlot = = slot){ return BAD _ VALUE;} ++当前;}
// 3.将插槽的状态转为空闲,这是以前获得的,并将该插槽添加到缓冲队列核心的mFreeBuffers列表中(关于mFreeBuffers的定义,请参考2.1中的介绍)。mbufferstate = = bufferslot::acquired){ mslots[slot]。megldisplay = egg mSlots[slot]。mEglFence = eglFencemSlots[slot]。mFence = releaseFencemSlots[slot]。mBufferState = BufferSlot::FREE;mCore->;mFreeBuffers.push_back(插槽);listener = Mcore->;mConnectedProducerListenerBQ _ LOGV(" release buffer:release slot % d ",slot);}
// 4.如果数据被消费掉了,回调生产者(监听器!= NULL) {侦听器->;onbufferrelized();}}
从上面的注释可以看出,releaseBuffer的主要步骤如下:
将Slot的状态扭转为FREE将被消费的Slot添加到mFreeBuffers供后续的生产者dequeueBuffer使用回调告知生产者有数据被消费,生产者可以准备生产数据了Summary releaseBuffer:将槽的状态变为FREE,添加到BufferQueueCore mFreeBuffers队列,最后通知生产者有数据出队列。
总结状态变化的过程:
以上主要介绍了BufferQueue的设计思想和内部实现。
以下将继续介绍BufferQueue,重点介绍BufferQueue在Android中的常见封装,以及BufferQueue在SurfaceView中的具体实现。
3.缓冲队列常用的封装类
在实际应用中,除了直接使用BufferQueue之外,更多的使用了Surface/SurfaceTexture,对Buffer Queue进行包装,方便业务更方便的使用Buffer Queue。Surface是BufferQueue的生产者,SurfaceTexture是BufferQueue的消费者。
3.1表面
曲面的构造函数如下:
表面::表面(
const sp<。IGraphicBufferProducer & gt& ampbufferProducer,
bool controlledByApp)
:MGRaphicBufferProducer(BufferProducer),
管理生成数(0)
构造函数需要传入一个生产者的引用,与BufferQueue的交互由这个生产者的引用完成。出列缓冲过程如下:
Int surface::出列缓冲区(Android _ native _ buffer _ t * * buffer,int * fencefd) {//1。调用mGraphicBufferProducer的出列缓冲区方法,并尝试获取一个槽索引int buf =-1;sp<。栅栏>。栅栏;status _ t result = MgRaphicBufferProducer->;出列缓冲区(& ampbuf & amp。栅栏、交换服务器零、请求宽度、请求高度、请求格式、请求使用);if(结果& lt0) { ALOGV("出列缓冲区:IGraphicBufferProducer::出列缓冲区(%d,%d,%d,%d,%d)" "失败:%d ",swapIntervalZero,reqWidth,reqHeight,reqFormat,reqUsage,result);返回结果;}
// 2.调用mGraphicBufferProducer的requestBuffer方法,并尝试获取slotsp
// 3.return GraphicBuffer * buffer = gbuf . get();}
QueueBuffer也如下,流程如下:
int Surface::queueBuffer(Android _ native _ buffer _ t * buffer,int fenceFd){ IgraphicBufferProducer::queueBufferOutput输出;igraphicbufferproducer::queuebufferinput输入(timestamp,isautotimestamp,mDataSpace,crop,mScalingMode,mttransform ^ mstickytransform,mSwapIntervalZero,fence,mstickytransform);// 1.直接调用mGraphicBufferProducer的queueBuffer方法获取status _ terr = mGraphicBufferProducer->;queueBuffer(i,input & amp;输出);if (err!= OK){ ALLOE(" queue buffer:错误排队缓冲到SurfaceTexture,%d ",err);}}
Surface还提供了一个锁函数来支持双缓冲,并在内部调用出列缓冲方法来获取最新的缓冲:
status _ t Surface::lock(anativwindow _ Buffer * outff er,Aret * InOutDirtybounds){ anativwindowbuffer * out;int fenceFd =-1;//1.获取实际的bufferstatus _ terr =出列缓冲区(&: out,ampfenceFd);
//2.处理双缓冲if(cancopyback){//复制无效的区域,不保留此round const region copy back(mdirtyregion。减法(new dirty region));if(!copy back . isempty())copy BLT(back buffer,frontBuffer,copy back);}}
Surface还提供了解锁和发布方法来将数据发送到缓冲队列:
status _ t Surface::unlock and post(){ if(mLockedBuffer = = 0){ ALLOE(" Surface::unlock and post失败,没有锁定的缓冲区");返回无效操作;} int FD =-1;status _ t err = mLockedBuffer->;解锁异步(& ampFD);ALOGE _ IF(错误,“解锁缓冲区(%p)失败”,MLockedBuffer->;手柄);//1.将生成的数据发送到buffer queueerr = queue buffer(mlockedbuffer)。get(),FD);ALOGE _ IF(错误,“queueBuffer(句柄=%p)失败(%s)”,MLockedBuffer->;handle,strerror(-err));mPostedBuffer = mLockedBuffermLockedBuffer = 0;返回err}
3.2表面纹理
作为BufferQueue的消费者,SurfaceTexture的初始化代码如下:
static void surface texture _ init(JNIEnv * env,jobject thiz,jboolean isDetached,jint texName,jboolean singleBufferMode,job object Weakthiz){ sp & lt;IGraphicBufferProducer & gt生产者;sp<。IGraphicBufferConsumer & gt消费者;//1.创建缓冲队列缓冲队列::createbuffer队列(&;制片人&。消费者);if(SingleBuffermode){ consumer->;disablesyncbuffer();消费者->;setDefaultMaxBufferCount(1);}
//2.创建一个使用者实例,表面纹理
消费方式是updateTexImage,流程如下:
静态void surface texture _ updateteximage(jnie NV * env,jobjectthiz) {//1。获取消费者服务点
// 2.调用updateTexImage方法status _ terr = surface texture->;UpdateTexImage方法();if(err = = INVALID _ OPERATION){ JnithRowexception(env,IllegalStateException,“无法更新纹理内容(有关详细信息,请参见“log cat”));} else if(err & lt;0){ jniThrowRuntimeException(env,“updateTexImage期间出错(有关详细信息,请参见log cat)”);}}
GLConsumer的UpdateTextImage实现如下:
status _ t GLconsumer::updateTextimage(){ Buffer item;//1.调用自己的acquireBufferLocked方法err = acquirebufferlocked (&: item,0);:updateTexImage() { //释放前一个缓冲区。err = updateAndresleelocked(item);if (err!= NO _ ERROR){ GlBindTexture(mTexTarget,mtex name);返回err}
}
AcquireBufferLocked方法,最后来到了ConsumerBase的acquireBufferLocked方法。
status _ tconsumer base::Acquire BufferLocked(buffer item * item,nsecs _ telepresence when,uint64 _ t maxframenumber) {//1。最后到达消费者的acquirebuffer方法,消费者对应上述bufferqueconsumer status _ terr = mconsumer-> acquire buffer(item,presentWhen,maxFrameNumber);if (err!= NO _ ERROR){ return ERR;}返回OK;}
同理,消费者消费数据的方法是releaseTexImage,最终会去BufferQueueConsumer的releaseBufferLocked方法,这里就不描述了。
4.4的实例。缓冲队列
上面描述了BufferQueue的内部实现和常用的封装类。接下来介绍一个具体的例子。
在Android中,SurfaceView是系统提供的组件,因为可以在子线程中绘制,提高性能。曲面视图有自己的曲面,不需要与活动的曲面共享。在曲面投影器中,活动的曲面和表面视图的曲面是水平的,彼此独立,可以独立合成。然后我们来看看SurfaceView是如何使用BufferQueue的。
4.1数据生产流程
这里不涉及曲面视图的曲面创建过程。如果你有兴趣,可以参考安卓SurfaceView的画图实现原理来分析这篇文章。我们主要关注与BufferQueue相关的绘制和显示步骤。
使用SuerfaceView绘制伪代码,如下所示:
Canvas画布= null
尝试{
canvas = holder . LockCanvas(null);
//实际绘制
}捕获(例外e) {
// TODO:处理异常
e . printstacktrace();
}最后{
if(canvas!= null) {
holder . unlock canvasandpost(canvas);
}
需要调用lockCanvas和unlockCanvasAndPost方法。这两种方法的功能是什么?
先看一下lockCanvas。呼叫过程如下:
SurfaceHolder.lockCanvasSurfaceHolder.internalLockCanvasSurface.lockCanvasSurface.nativeLockCanvasnativeLockCanvas实现如下:
静态jlong nativeLockCanvas(JNIEnv * env,jclass clazz,jlong nativeObject,jobject canvasObj,job object DirTyrctobj){ sp & lt;Surface>。surface(re interpret _ cast & lt;表面*>。(native object));自动窗口_缓冲区溢出;//1.通过Surface::lock方法获取一个合适的bufferstatus _ terr = surface->函数;锁定(& ampexpowffer,dirtyRectPtr);
//2.构造一个位图,它的地址指向在步骤1中获得的缓冲区的地址,这样在这个位图上绘制的内容直接被绘制到图形缓冲区。如果GraphicBuffer的内存是由SurfaceFlinger通过共享内存请求的,那么SurfaceFlinger就可以直接看到绘制的图形数据skipage info = skipage info::make(outwaffer . width,outbuffer.height,convert pixel format(outwaffer . format),kpemul _ skalphatype);SkBitmap位图;ssize _ t BPR = expowffer . stride * Bytesperppixel(expowffer . format);bitmap.setInfo(info,BPR);if(expowffer . width > 1;0 & amp& amp突出高度。0){ bitmap . set pixels(expowffer . bits);} else { //使用空位图比较安全。位图.设置像素(空);}
// 3.将创建的位图设置为画布画布画布*原生画布=图形JNI:: getnativecanvas (env,Canvas obj);nativeCanvas->;setBitmap(位图);}
从这里可以看出,nativeLockCanvas的步骤主要如下:
通过调用Surface::lock方法(内部也是调用dequeueBuffer和requestBuffer方法),获取到一个GraphicBuffer将步骤1获取的GraphicBuffer构造成一个Bitmap,设置给Canvas应用通过这个Canvas就可以绘制图形了绘制完图形后,调用unlockCanvasAndPost方法。调用过程如下:
SurfaceHolder.unlockCanvasAndPostSurface.unlockCanvasAndPostSurface.nativeUnlockCanvasAndPostnativeUnlockCanvasAndPost实现如下:
静态void nativenockcanvasandpost(JNIEnv * env,jclass clazz,
jlong nativeObject,job object canvasObj){
sp<。Surface>。surface(re interpret _ cast & lt;表面*>。(native object));
if(!isSurfaceValid(surface)) {
返回;
}
//将画布从表面分离
canvas * nativeCanvas = GraphicsJNI::getNativeCanvas(env,canvasObj);
nativeCanvas->;setBitmap(SkBitmap());
//直接调用Surface的unlockAndPost方法。从上面可以看出,unlockAndPost内部最终会调用qeueBuffer方法
status_t err = surface->UnlockAndPost方法,从上面可以看出,unlockAndPost内部最终会调用qeueBuffer方法();
if(err & lt;0) {
doThrowIAE(env);
}
}
从注释中可以看出,这个方法最终会调用Surface的unlockAndPost方法,BufferQueueProducer的queueBuffer方法最终会在这个方法内部调用。也就是说完成了数据生产和团队录入。
4.2数据消费流程
SurfaceView绘制的数据经过BufferQueue后,最终由SurfaceFlinger合成并消耗。SurfaceFlinger的消耗是通过surfaceflinger实现的,流程如下:
status _ t surface flinger consumer::updateTextimage(Bufferrejecter * rejecter,const DispSync & ampdispSync,uint 64 _ t MaxFramenumber){ Buffer item;// 1.调用acquireBufferLocked以获取slot err = AcquireBufferLocked(&;item,computeExpectedPresent(dispSync),maxFrameNumber);if (err!= NO _ ERROR){ return ERR;}
//2.消费后,释放slot err = updateandreleaselocked(item);if (err!= NO _ ERROR){ return ERR;}}
AcquireBufferLocked的实现如下:
status _ t surface flinger consumer::acquire buffer locked(buffer item * item,nsecs_t presentWhen,Uint64_t maxFrameNumber) {//1 .调用GL consumer::acquirebufferlocked,最后调用BufferQueueConsumer的acquire buffer方法status _ tresult = GL consumer::acquire bufferlocked(item,present when,max frame number);if(result = = NO _ ERROR){ mTransformToDisplayInverse = item-& gt;mTransformToDisplayInversemsurfacedars = item->;mSurfaceDamage}返回结果;}
更新和删除锁定方法的流程如下:
status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item){// Do whatever sync ops we need to do before releasing the old slot.err = syncForReleaseLocked(mEglDisplay);if (err != NO_ERROR) {//1. releaseBufferLocked释放Slot,最终会调用到BufferQueueConsumer的releaseBuffer方法releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,mEglDisplay, EGL_NO_SYNC_KHR);return err;}}5.摘要
介绍了缓冲队列的内部实现,用入队/入队对说明了缓冲队列内部槽的状态反转过程,并介绍了常用的缓冲队列封装类。最后,介绍了一个基于缓冲队列的例子。
1.《releasebuffer 深入浅出Android BufferQueue》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《releasebuffer 深入浅出Android BufferQueue》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/guonei/1290726.html