在前面一篇文章中,我们分析了Android应用程序请求SurfaceFlinger服务创建Surface的过程。有了Surface之后,Android应用程序就可以在上面绘制自己的UI了,接着再请求SurfaceFlinger服务将这个已经绘制好了UI的Surface渲染到设备显示屏上去。在本文中,我们就将详细分析Android应用程序请求SurfaceFlinger服务渲染Surface的过程。
Android应用程序在请求SurfaceFlinger服务渲染一个Surface之前,首先要将该Surface作为当前活动的绘图上下文,以便可以使用OpengGL库或者其它库的API来在上面绘制UI,我们以Android系统的开机动画应用程序bootanim为例,来说明这个问题。
从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道,Android系统的开机动画应用程序bootanim是在BootAnimation类的成员函数readyToRun中请求SurfaceFlinger服务创建Surface的。这个Surface创建完成之后,就会被设置为当前活动的绘图上下文,如下所示:
status_t BootAnimation::readyToRun() { ...... // create the native surface sp<SurfaceControl> control = session()->createSurface( getpid(), 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); ...... sp<Surface> s = control->getSurface(); ...... // initialize opengl and egl const EGLint attribs[] = { EGL_DEPTH_SIZE, 0, EGL_NONE }; ...... EGLConfig config; EGLSurface surface; EGLContext context; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); EGLUtils::selectConfigForNativeWindow(display, attribs, s.get(), &config); surface = eglCreateWindowSurface(display, config, s.get(), NULL); context = eglCreateContext(display, config, NULL, NULL); ...... if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) return NO_INIT; ......}
BootAnimation类的成员函数readyToRun首先调用eglGetDisplay和eglInitialize函数来获得和初始化OpengGL库的默认显示屏,接着再调用EGLUtils::selectConfigForNativeWindow函数来获得前面所创建的一个Surface(由sp<Surface>指针s来描述)的配置信息。有了这些信息之后,接下来就分别调用eglCreateWindowSurface和eglCreateContext函数来创建一个适用于OpenGL库使用的绘图表面surface以及绘图上下文context,最后就可以调用eglMakeCurrent函数来将绘图表面surface和绘图上下文context设置为当前活动的绘图表面和绘图上下文,这就相当于是将前面请求SurfaceFlinger服务创建的一个Surface设置为当前活动的缓图上下文了。
完成了上述操作之后,Android系统的开机动画应用程序bootanim就可以继续使用OpengGL库的其它API来在当前活动的Surface上绘制UI了,不过,通过前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文的学习,我们知道,此时SurfaceFlinger服务为Android应用程序创建的Surface只有UI元数据缓冲区,而没有UI数据缓冲区,即还没有图形缓冲区,换句来说,就是还没有可以用来绘制UI的载体。那么,这些用来绘制UI的图形缓冲区是什么时候创建的呢?
从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文可以知道,每一个Surface都有一个对应的UI元数据缓冲区堆栈,这个UI元数据缓冲区堆栈是使用一个SharedBufferStack来描述的,如图1所示。
图1 SharedBufferStack的结构示意图
从图1就可以看出,每一个UI元数据缓冲区都可能对应有一个UI数据缓冲区,这个UI数据缓冲区又可以称为图形缓冲区,它使用一个GraphicBuffer对象来描述。注意,一个UI元数据缓冲区只有第一次被使用时,Android应用程序才会为它创建一个图形缓冲区,因此,我们才说每一个UI元数据缓冲区都可能对应有一个UI数据缓冲区。例如,在图1中,目前只使到了编号为1和2的UI元数据缓冲区,因此,只有它们才有对应的图形缓冲区,而编号为3、4和5的UI元数据缓冲区没有。
Android应用程序渲染一个Surface的过程大致如下所示:
1. 从UI元数据缓冲区堆栈中得到一个空闲的UI元数据缓冲区;
2. 请求SurfaceFlinger服务为这个空闲的UI元数据缓冲区分配一个图形缓冲区;
3. 在图形缓冲区上面绘制好UI之后,即填充好UI数据之后,就将前面得到的空闲UI元数据缓冲区添加到UI元数据缓冲区堆栈中的待渲染队列中去;
4. 请求SurfaceFlinger服务渲染前面已经准备好了图形缓冲区的Surface;
5. SurfaceFlinger服务从即将要渲染的Surface的UI元数据缓冲区堆栈的待渲染队列中找到待渲染的UI元数据缓冲区;
6. SurfaceFlinger服务得到了待渲染的UI元数据缓冲区之后,接着再找到在前面第2步为它所分配的图形缓冲区,最后就可以将这个图形缓冲区渲染到设备显示屏上去。
这个过程的第1步、第3步和第5步涉到UI元数据缓冲区堆栈的一些出入栈操作,为了方便后面描述Android应用程序请求SurfaceFlinger服务渲染Surface的过程,我们首先介绍一下UI元数据缓冲区堆栈的一些出入栈操作。
在前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文中,我们分析了用来描述UI元数据缓冲区堆栈的SharedBufferServer和SharedBufferClient类的父类SharedBufferBase,它有一个成员函数waitForCondition,用来等待一个条件得到满足,它定义在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
class SharedBufferBase{ ......protected: ...... struct ConditionBase { SharedBufferStack& stack; inline ConditionBase(SharedBufferBase* sbc) : stack(*sbc->mSharedStack) { } virtual ~ConditionBase() { }; virtual bool operator()() const = 0; virtual const char* name() const = 0; }; status_t waitForCondition(const ConditionBase& condition); ......};SharedBufferBase类的成员函数waitForCondition只有一个参数condition,它的类型为ConditionBase,用来描述一个需要等待满足的条件。ConditionBase类是一个抽象类,我们需要以它来为父类,来实现一个自定义的条件,并且重写操作符号()和成员函数name。接下来,我们分析SharedBufferBase类的成员函数waitForCondition的实现,接着再分析ConditionBase类的一个子类的实现。
SharedBufferBase类的成员函数waitForCondition实现在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
status_t SharedBufferBase::waitForCondition(const ConditionBase& condition){ const SharedBufferStack& stack( *mSharedStack ); SharedClient& client( *mSharedClient ); const nsecs_t TIMEOUT = s2ns(1); const int identity = mIdentity; Mutex::Autolock _l(client.lock); while ((condition()==false) && (stack.identity == identity) && (stack.status == NO_ERROR)) { status_t err = client.cv.waitRelative(client.lock, TIMEOUT); // handle errors and timeouts if (CC_UNLIKELY(err != NO_ERROR)) { if (err == TIMED_OUT) { if (condition()) { LOGE("waitForCondition(%s) timed out (identity=%d), " "but condition is true! We recovered but it " "shouldn't happen." , condition.name(), stack.identity); break; } else { LOGW("waitForCondition(%s) timed out " "(identity=%d, status=%d). " "CPU may be pegged. trying again.", condition.name(), stack.identity, stack.status); } } else { LOGE("waitForCondition(%s) error (%s) ", condition.name(), strerror(-err)); return err; } } } return (stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;}SharedBufferBase类的成员变量mSharedStack指向了一个SharedBufferStack对象,即一个UI元数据缓冲区堆栈,另外一个成员变量mSharedClient指向了当前应用程序进程的一个SharedClient单例。
SharedClient类有一个类型为Condition的成员变量cv,用来描述一个条件变量,同时,SharedClient类还有一个类型为Mutex的成员变量lock,用来描述一个互斥锁。通过调用一个Condition对象的成员函数waitRelative,就可以在指定的时间内等待一个互斥锁变为可用。
SharedBufferBase类的成员函数waitForCondition中的while循环的作用是循环等待一个UI元数据缓冲区堆栈满足某一个条件,这个条件是通过参数condition来描述的。当调用参数condition所描述的一个CondtitionBase对象的重载操作符号()的返回值等于true的时候,就表示所要等待的条件得到满足了,这时候函数就会停止执行中间的while循环语句。另一方面,当调用参数condition所描述的一个CondtitionBase对象的重载操作符号()的返回值等于flase的时候,就表示所要等待的条件还没有得到满足,这时候函数就会继续执中间的while循环,直到所要等待的条件得到满足为止。等待的操作是通过调用下面这个语句来完成的:
status_t err = client.cv.waitRelative(client.lock, TIMEOUT);即调用当前应用程序进程的SharedClient单例client的成员变量cv所描述的一个条件变量的成员函数waitRelative来完成,并且指定要等待的互斥锁为当前应用程序进程的SharedClient单例client的成员变量lock所描述的一个互斥锁,以及指定等待的时间为TIMEOUT,即1秒。如果在1秒内,当前应用程序进程的SharedClient单例client的成员变量lock所描述的一个互斥锁还是不可用,那么上述等待操作就会超时,然后导致重新执行外层的while循环,否则的话,等待操作就完成了。
在SharedBufferClient类中,定义了一个ConditionBase子类DequeueCondition,用来描述一个UI元数据缓冲区堆栈是否有空闲的缓冲区可以出栈,它定义在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
class SharedBufferClient : public SharedBufferBase{ ......private: ...... struct DequeueCondition : public ConditionBase { inline DequeueCondition(SharedBufferClient* sbc); inline bool operator()() const; inline const char* name() const { return "DequeueCondition"; } }; ......};一个UI元数据缓冲区堆栈是否有空闲的缓冲区可以出栈是由DequeueCondition类的重载操作符号()来决定的,它实现在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
bool SharedBufferClient::DequeueCondition::operator()() const { return stack.available > 0;}DequeueCondition类的成员变量stack是从父类ConditionBase继承下来的,它指向了一个SharedBufferStack对象,即用来描述一个UI元数据缓冲区堆栈。从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文可以知道,当一个SharedBufferStack对象的成员变量available的值大于0的时候,就说明它所描述的UI元数据缓冲区堆栈有空闲的缓冲区可以使用,因此,这时候DequeueCondition类的重载操作符号()的返回值就等于true,表示一个UI元数据缓冲区堆栈有空闲的缓冲区可以出栈。
SharedBufferBase类还有一个成员函数updateCondition,用来操作一个UI元数据缓冲区堆栈,例如,执行一个UI元数据缓冲区的出入栈操作。这个成员函数定义和实现在文件rameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
class SharedBufferBase{ ......protected: ...... struct UpdateBase { SharedBufferStack& stack; inline UpdateBase(SharedBufferBase* sbb) : stack(*sbb->mSharedStack) { } }; template <typename T> status_t updateCondition(T update);};template <typename T>status_t SharedBufferBase::updateCondition(T update) { SharedClient& client( *mSharedClient ); Mutex::Autolock _l(client.lock); ssize_t result = update(); client.cv.broadcast(); return result;}SharedBufferBase类的成员函数updateCondition是一个模板函数,它过调用参数T的重载操作符号()来实现一个具体的UI元数据缓冲区堆栈操作。这个参数T必须要从基类UpdateBase继承下来,并且重载操作符号()。
SharedBufferBase类的成员函数updateCondition执行完成一个UI元数据缓冲区堆栈操作之后,还会调用当前应用进程的SharedClient单例client的成员变量cv所描述的一个条件变量的成员函数broadcast,用来唤醒那些在当前应用进程的SharedClient单例client的成员变量lock所描述的一个互斥锁上等待的其它线程,以便它们可以继续执行自己的操作,这样,SharedBufferBase类的成员函数updateCondition就可以和前面介绍的成员函数waitCondition对应起来。
接下来,我们就分别分析UpdateBase的三个子类QueueUpdate、DequeueUpdate和RetireUpdate。QueueUpdate和DequeueUpdate两个子类是Android应用程序这一侧使用的,前者用来向一个UI元数据缓冲区堆栈的待渲染队列增加一个缓冲区,而后者用来从一个UI元数据缓冲区堆栈出栈一个空闲的缓冲区。RetireUpdate类是在SurfaceFlinger服务这一侧使用的,用来从一个UI元数据缓冲区堆栈的待渲染队列出栈一个缓冲区,以便可以将与它所对应的图形缓冲区渲染到设备显示屏去。
QueueUpdate类定义在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
class SharedBufferClient : public SharedBufferBase{ ......private: ...... struct QueueUpdate : public UpdateBase { inline QueueUpdate(SharedBufferBase* sbb); inline ssize_t operator()(); }; ......};它的重载操作符号()实现在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
ssize_t SharedBufferClient::QueueUpdate::operator()() { android_atomic_inc(&stack.queued); return NO_ERROR;}QueueUpdate类的成员变量stack是从父类UpdateBase继承下来的,它指向了一个SharedBufferStack对象,用来描述当前要操作的UI元数据缓冲区堆栈。从前面的图1可以知道,当我们将一个SharedBufferStack对象的成员变量queued的值增加1的时候,就表示这个SharedBufferStack对象所描述的UI元数据缓冲区堆栈的待渲染队列的大小增加了1。不过,在执行这个操作之前,我们还需要将用来这个待渲染队列头queue_head往前移动一个位置。后面在分析Surface的渲染过程时,我们再详细分析。
DequeueUpdate类定义在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
class SharedBufferClient : public SharedBufferBase{ ......private: ...... struct DequeueUpdate : public UpdateBase { inline DequeueUpdate(SharedBufferBase* sbb); inline ssize_t operator()(); }; ......};它的重载操作符号()实现在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
ssize_t SharedBufferClient::DequeueUpdate::operator()() { if (android_atomic_dec(&stack.available) == 0) { LOGW("dequeue probably called from multiple threads!"); } return NO_ERROR;}DequeueUpdate类的成员变量stack是从父类UpdateBase继承下来的,它指向了一个SharedBufferStack对象,用来描述当前要操作的UI元数据缓冲区堆栈。从前面的图1可以知道,当我们将一个SharedBufferStack对象的成员变量available的值减少1的时候,就表示这个SharedBufferStack对象所描述的UI元数据缓冲区堆栈的空闲缓冲区的大小就减少了1。不过,在执行这个操作之前,我们还需要将用来这个UI元数据缓冲区堆栈尾tail往前移动一个位置。后面在分析Surface的渲染过程时,我们再详细分析。
RetireUpdate类定义在文件frameworks/base/include/private/surfaceflinger/SharedBufferStack.h中,如下所示:
class SharedBufferServer : public SharedBufferBase, public LightRefBase<SharedBufferServer>{ ......private: ...... struct RetireUpdate : public UpdateBase { const int numBuffers; inline RetireUpdate(SharedBufferBase* sbb, int numBuffers); inline ssize_t operator()(); }; ......};RetireUpdate类的成员变量numBuffers用来描述一个UI元数据缓冲区堆栈的大小,它的重载操作符号()实现在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp文件中,如下所示:
ssize_t SharedBufferServer::RetireUpdate::operator()() { int32_t head = stack.head; if (uint32_t(head) >= SharedBufferStack::NUM_BUFFER_MAX) return BAD_VALUE; // Decrement the number of queued buffers int32_t queued; do { queued = stack.queued; if (queued == 0) { return NOT_ENOUGH_DATA; } } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); // lock the buffer before advancing head, which automatically unlocks // the buffer we preventively locked upon entering this function head = (head + 1) % numBuffers; const int8_t headBuf = stack.index[head]; stack.headBuf = headBuf; // head is only modified here, so we don't need to use cmpxchg android_atomic_write(head, &stack.head); // now that head has moved, we can increment the number of available buffers android_atomic_inc(&stack.available); return head;}在前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文中提到,在图1所描述的UI元数据缓冲区堆栈中,位于(head, queue_head]里面的缓冲区组成了一个待渲染队列,而SurfaceFlinger服务就是按照head到queue_head的顺序来渲染这个队列中的缓冲区的。理解了这一点之后,RetireUpdate类的重载操作符号()的实现就好理解了。
首先,函数使用一个do...while循环来将queued的值减少1,即将待渲染队列的大小减少1。当然,如果这个待渲染队列的大小本来就等于0,那么函数就什么也不做就返回了。接着,函数将待渲染队列的头部head向前移一个位置。移动后的得到的位置所对应的缓冲区就是接下来要渲染的,因此,函数最后要将它返回给调用者。函数在将要渲染的缓冲区的位置返回给调用者之前,还会将当前正在操作的UI元数据缓冲区的空闲缓冲区的个数available增加1。
至此,DequeueCondition、QueueUpdate、DequeueUpdate和RetireUpdate这四个辅助类就介绍完成了,接下来,我们就可以继续分析Android应用程序请求SurfaceFlinger服务渲染Surface的过程了。在分析的过程中,我们还会继续看到这四个辅助类的使用方法。
在前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文的Step 16中,我们将在Android应用程序这一侧所创建的一个Surface的父类ANativeWindow的OpenGL回调函数dequeueBuffer和queueBuffer分别设置为Surface类的静态成员函数dequeueBuffer和queueBuffer。OpenGL在绘图之前,就首先会调用Surface类的静态成员函数dequeueBuffer来获得一个空闲的UI元数据缓冲区,接着请求SurfaceFlinger服务为这个UI元数据缓冲区分配一个图形缓冲区。有了图形缓冲区之后,OpengGL库就可以往里面填入UI数据。在往图形缓冲区填入UI数据的同时,OpenGL库也会往前面获得的UI元数据缓冲区填入当前正在操作的Surface的裁剪区域、纹理坐标和旋转方向等信息。再接下来,OpenGL库就会调用Surface类的静态成员函数queueBuffer来将前面已经填好了数据的UI元数据缓冲区添加到当前正在操作的Surface的UI元数缓冲区堆栈的待渲染队列中。最后,Android应用程序就会请求SurfaceFlinger服务将当前正在操作的Surface的UI数据渲染到设备显示屏去。
接下来,我们就首先分析Surface类的静态成员函数dequeueBuffer的实现,接着再分析Surface类的静态成员函数queueBuffer的实现,最后分析SurfaceFlinger服务渲染Surface的图形缓冲区的过程。
Surface类的静态成员函数dequeueBuffer获得空闲UI元数据缓冲区,以及请求SurfaceFlinger服务为这个空闲UI元数据缓冲区分配图形缓冲区的过程如图2所示:
图2 分配空闲UI元数据缓冲区及其图形缓冲区的过程
这个过程一共分为12个步骤,接下来我们就详细分析每一个步骤。
Step 1. Surface.dequeueBuffer
int Surface::dequeueBuffer(ANativeWindow* window, android_native_buffer_t** buffer) { Surface* self = getSelf(window); return self->dequeueBuffer(buffer);}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
参数window虽然是一个ANativeWindow指针,但是它实际上指向的是一个Surface对象,因此,函数首先调用另外一个静态成员函数getSelf来将它转换为一个Surface对象self,接着再调用这个Surface对象self的成员函数dequeueBuffer来分配一个空闲UI元数据缓冲区和一个图形缓冲区,其中,分配的图形缓冲区就保存在输出参数buffer中。
Surface类的非静态成员函数dequeueBuffer的实现如下所示:
int Surface::dequeueBuffer(android_native_buffer_t** buffer){ status_t err = validate(); if (err != NO_ERROR) return err; ...... ssize_t bufIdx = mSharedBufferClient->dequeue(); ...... if (bufIdx < 0) { ...... return bufIdx; } // grow the buffer array if needed const size_t size = mBuffers.size(); const size_t needed = bufIdx+1; if (size < needed) { mBuffers.insertAt(size, needed-size); } uint32_t w, h, format, usage; if (needNewBuffer(bufIdx, &w, &h, &format, &usage)) { err = getBufferLocked(bufIdx, w, h, format, usage); ...... if (err == NO_ERROR) { // reset the width/height with the what we get from the buffer const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); mWidth = uint32_t(backBuffer->width); mHeight = uint32_t(backBuffer->height); } } // if we still don't have a buffer here, we probably ran out of memory const sp<GraphicBuffer>& backBuffer(mBuffers[bufIdx]); if (!err && backBuffer==0) { err = NO_MEMORY; } if (err == NO_ERROR) { mDirtyRegion.set(backBuffer->width, backBuffer->height); *buffer = backBuffer.get(); } else { mSharedBufferClient->undoDequeue(bufIdx); } return err;}
这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
函数首先调用Surface类的成员变量mSharedBufferClient所指向的一个SharedBufferClient对象的成员函数dequeue来从UI元数据缓冲区堆栈中获得一个空闲的缓冲区。获得的空闲缓冲区使用一个编号来描述,这个编号保存在变量bufIdx中。后面我们再分析SharedBufferClient类的成员函数dequeue的实现。
获最一个空闲UI元数据缓冲区之后,函数接下来判断该缓冲区的编号是否大于Surface类的成员变量mBuffers所描述的一个GraphicBuffer向量的大小。如果大于,那么就需要扩充这个向量的大小,以便后面可以用来保存与该缓冲区对应的一个GraphicBuffer,即一个图形缓冲区。
函数再接下来调用Surface类的另外一个成员函数needNewBuffer来判断之前是否已经为编号为bufIdx的UI元数据缓冲区分配过图形缓冲区了,它的实现如下所示:
bool Surface::needNewBuffer(int bufIdx, uint32_t *pWidth, uint32_t *pHeight, uint32_t *pFormat, uint32_t *pUsage) const{ Mutex::Autolock _l(mSurfaceLock); // Always call needNewBuffer(), since it clears the needed buffers flags bool needNewBuffer = mSharedBufferClient->needNewBuffer(bufIdx); bool validBuffer = mBufferInfo.validateBuffer(mBuffers[bufIdx]); bool newNeewBuffer = needNewBuffer || !validBuffer; if (newNeewBuffer) { mBufferInfo.get(pWidth, pHeight, pFormat, pUsage); } return newNeewBuffer;}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
由于UI元数据缓冲区堆栈中的缓冲区是循环使用的。当一个UI元数据缓冲区第一次被使用的时候,应用程序就会请求SurfaceFlinger服务为它分配一个图形缓冲区。这个图形缓冲区使用完成之后,就会被应用程序缓存起来,以便后面可以继续使用。但是这个图形缓冲区可能会被得无效,例如,与它对应的Surface的大小和用途等信息发生改变之后,该图形缓冲区就会变得无效了,因为它在分配的时候,是按照既定的大小和用途来分配的。
这个函数首先调用Surface类的成员变量mSharedBufferClient所指向的一个SharedBufferClient对象的成员函数needBuffer来验证编号为bufIdx的UI元数据缓冲区所对应的图形缓冲区信息是否发生了变化。如果发生了变化,那么变量needNewBuffer的值就会等于true,表示要重新为编号为bufIdx的UI元数据缓冲区分配新的图形缓冲区。
SharedBufferClient类的成员函数needBuffer的实现如下所示:
bool SharedBufferClient::needNewBuffer(int buf) const{ SharedBufferStack& stack( *mSharedStack ); const uint32_t mask = 1<<(31-buf); return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0;}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
SharedBufferClient类的成员变量mSharedStack的类型为SharedBufferStack,它是从父类SharedBufferBase继承下来的,用来描述一个UI元数据缓冲区堆栈。SharedBufferStack类的成员变量reallocMask是一个掩码,如果它的某一位的值等于1,那么这一位所描述的一个UI元数据缓冲区所对应的图形缓冲区就是无效的。这一般是由SurfaceFlinger服务来设备的。当SurfaceFlinger服务发现一个Surface的元信息发生变化时,就会通过一个SharedBufferServer对象来设置这个Surface的UI元数据缓冲区堆栈的成员变量reallocMask的相应位等于1,以便应用程序在使用到该位所描述的UI元数据缓冲区时,请求分配一个新的图形缓冲区。例如,假设SharedBufferStack类的成员变量reallocMask的值等于01000000 00000000 00000000 00000000,那么就表示编号为1的UI元数据缓冲区对应的图形缓冲区需要重新分配。
回到Surface类的成员函数needNewBuffer中,接下来该函数继续验证编号为bufIdx对应的UI元数据缓冲区在成员变量mBuffers中所对应的图形缓冲区是否还有效,即图形缓冲区mBuffers[bufIdx]是否还有效,这是通过调用Surface类的成员变量mBufferInfo所描述的一个BufferInfo对象的成员函数validateBuffer来验证的。如果没有效,那么变量validBuffer的值就会等于false,表示要重新为编号为bufIdx的UI元数据缓冲区分配新的图形缓冲区。
BufferInfo类的成员函数validateBuffer的实现如下所示:
bool Surface::BufferInfo::validateBuffer(const sp<GraphicBuffer>& buffer) const { // make sure we AT LEAST have the usage flags we want if (mDirty || buffer==0 || ((buffer->usage & mUsage) != mUsage)) { mDirty = 0; return false; } return true;}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
BufferInfo类的成员变量mDirty用来描述一个Surface的元数据是否发了变化,例如,它的大小、像素格式等是发生了变化。如果发生了变化,那么它的值就会不等于0。
参数buffer是一个类型为GraphicBuffer的强指针,如果它的值等于null,那么就说明它所描述的图形缓冲是无效的。
如果参数buffer所指向的图形缓冲区是有效的,但是它的用途发生了变化,即它的用途与它所对应的Surface的用途已经不一致了。
上述三种情况都说明需要为编号为bufIdx的UI元数据缓冲分配新的图形缓冲区,因此,这个函数的返回值就会等于false。
回到Surface类的成员函数needNewBuffer中,接下来该函数通过变量needNewBuffer和变量validBuffer的值就可以知道是否需要为编号为bufIdx的UI元数据缓冲分配新的图形缓冲区了。假设应用程序是第一次使用编号为bufIdx的UI元数据缓冲,那么变量validBuffer的值就一定会等于false,因此,Surface类的成员函数needNewBuffer的返回值就会等于true,表示要为编号为bufIdx的UI元数据缓冲分配新的图形缓冲区。该函数在返回之前,还会通过Surface类的成员变量mBufferInfo所描述的一个BufferInfo对象来得到当前正在绘制的Surface的宽度、高度、像素格式以及用途,分别保存在输出参数pWidth、pHeight、pFormat和pUsage,以便应用程序接下来可以使用这些信息来请求SurfaceFlinger服务分配一个新的图形缓冲区。
回到Surface类的非静态成员函数dequeueBuffer中,该函数接下来就会调用Surface类的另外一个成员函数getBufferLocked来请求SurfaceFlinger服务为编号为bufIdx的UI元数据缓冲区分配一个图形缓冲区。分配完成之后,这个图形缓冲区就会保存在mBuffers[bufIdx]中。后面我们就详细分析Surface类的成员函数getBufferLocked的实现。
Surface类的非静态成员函数dequeueBuffer获得了编号为bufIdx的图形缓冲区之后,接下来就会得到这个图形缓冲区的宽度和高度,并且保存Surface类的成员变量mWidth和mHeight中,以便可以表示当前下在绘制的Surface的宽度和高度。同时,这个图形缓冲区的宽度和高度还会被更新到用来描述当前正在绘制的Surface的裁剪区域去,因为SurfaceFlinger服务在渲染该Surface时,需要用到这个信息。当前正在绘制的Surface的裁剪区域是由Surface类的成员变量mDirtyRegion来描述的,只要调用它的成员函数set,就可以重新设置它的宽度和高度。
最后,Surface类的非静态成员函数dequeueBuffer就将得到的图形缓冲区的地址保存输出参数buffer中,以便OpenGL库可以在上面填入UI数据。另一方面,如果分配图形缓冲区失败,那么Surface类的非静态成员函数dequeueBuffer会将前面得到的一个UI元数据缓冲区返回给成员变量mSharedBufferClient所描述的一个UI元数据缓冲区堆栈去,这是通过调用成员变量mSharedBufferClient的成员函数undoDequeue来实现的。
接下来,我们就继续分析SharedBufferClient类的成员函数dequeue的实现,以便了解它是如何从UI元数据缓冲区堆栈中获得一个空闲的缓冲区的。
Step 2. SharedBufferClient.dequeue
ssize_t SharedBufferClient::dequeue(){ SharedBufferStack& stack( *mSharedStack ); if (stack.head == tail && stack.available == mNumBuffers) { LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", tail, stack.head, stack.available, stack.queued); } RWLock::AutoRLock _rd(mLock); const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); //LOGD("[%d] about to dequeue a buffer", // mSharedStack->identity); DequeueCondition condition(this); status_t err = waitForCondition(condition); if (err != NO_ERROR) return ssize_t(err); DequeueUpdate update(this); updateCondition( update ); int dequeued = stack.index[tail]; tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1); LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail++=%d, %s", dequeued, tail, dump("").string()); mDequeueTime[dequeued] = dequeueTime; return dequeued;}
这个函数定义在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文可以知道,SharedBufferClient类的成员变量tail指向了一个UI元数据缓冲区堆栈的空闲缓冲区列表的尾部。当这个UI元数据缓冲区堆栈的可用空闲缓冲区的数量available的值大于0的时候,应用程序就可以从它的空闲缓冲区列表的尾部分配一个绘冲区出来使用。
函数首先创建了一个DequeueCondition对象condition,然后再调用SharedBufferClient从SharedBufferBase类继承下来的成员函数waitForCondition来判断当前正在使用的UI元数据缓冲区堆栈是否有空闲的缓冲区可以分配。如果没有,那么当前线程就会一直等待,直到可以得到一个空闲缓冲区为止。
函数接着创建了一个DequeueUpdate对象update,然后再调用SharedBufferClient从SharedBufferBase类继承下来的成员函数updateCondition来减少当前正在使用的UI元数据缓冲区堆栈的空闲缓冲区的数量,因为接下来要将空闲缓冲区列表尾部的缓冲区分配出来使用。
函数最后就通过SharedBufferClient类的成员变量tail来获得了一个编号为dequeued的空闲UI元数据缓冲区,并且将这个编号返回给调用者。不过,在返回之前,函数还会将SharedBufferClient类的成员变量tail向前移一个位置,以便它可以指向下一个可以用来分配的空闲UI元数据缓冲区。由于UI元数据缓冲区堆栈是循环使用的,因此,当SharedBufferClient类的成员变量tail向前移一个位置,即加1之后,它的值大于等于UI元数据缓冲区堆栈的大小mNumBuffers时,就需要绕回到堆栈的开头去。
这一步执行完成之后,就返回到Step 1中,即Surface类的成员函数dequeueBuffer中,这时候应用程序就为当前正在绘制的Surface获得了一个空闲UI元数据缓冲区,接下来就会继续调用Surface类的成员函数getBufferLocked来为该空闲UI元数据缓冲区分配图形缓冲区。
Step 3. Surface.getBufferLocked
status_t Surface::getBufferLocked(int index, uint32_t w, uint32_t h, uint32_t format, uint32_t usage){ sp<ISurface> s(mSurface); if (s == 0) return NO_INIT; status_t err = NO_MEMORY; // free the current buffer sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); if (currentBuffer != 0) { getBufferMapper().unregisterBuffer(currentBuffer->handle); currentBuffer.clear(); } sp<GraphicBuffer> buffer = s->requestBuffer(index, w, h, format, usage); ...... if (buffer != 0) { // this should never happen by construction ...... err = mSharedBufferClient->getStatus(); ...... if (!err && buffer->handle != NULL) { err = getBufferMapper().registerBuffer(buffer->handle); ...... if (err == NO_ERROR) { currentBuffer = buffer; currentBuffer->setIndex(index); } } else { err = err<0 ? err : status_t(NO_MEMORY); } } return err;}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道, Surface类的成员变量mSurface指向了一个类型为BpSurface的Binder代理对象,这个Binder代理对象引用了运行在SurfaceFlinger服务一侧的一个类型为SurfaceLayer的Binder本地对象。函数首先将这个成员变量保存在变量s中,后面会通过它来向SurfaceFlinger服务为编号为index的空闲UI元数据缓冲区分配一个图形缓冲区。
在请求SurfaceFlinger服务为编号为index的空闲UI元数据缓冲区分配图形缓冲区之前,函数还会检查在Surface类的成员变量mBuffers中是否存在一个与编号为index的空闲UI元数据缓冲区对应的图形缓冲区。如果存在的话,就需要将这个图形缓冲区从应用程序进程的地址空间注销掉,因为这个图形缓冲区已经变成无效了。Surface类的成员函数getBufferMapper的返回值是一个GraphicBufferMapper对象,通过调用这个GraphicBufferMapper对象的成员函数unregisterBuffer就可以注销一个指定的图形缓冲区。GraphicBufferMapper类的成员函数unregisterBuffer最终也是通过HAL层中的Gralloc模块提供的接口gralloc_unregister_buffer来注销一个指定的图形缓冲区,这一点可以参考前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文。
函数接下来就请求变量s所指向的一个BpSurface对象的成员函数requestBuffer请求SurfaceFlinger服务为编号为index的空闲UI元数据缓冲区分配一个图形缓冲区,这个缓冲区保存在变量buffer中。应用程序得到图形缓冲区buffer之后,还需要将它注册到本进程的地址空间之后,才能使用,这是通过调用GraphicBufferMapper类的成员函数registerBuffer来实现的,后面我们再详细分析这个注册的过程。
接下来,函数就将图形缓冲区buffer保存在一个GraphicBuffer引用currentBuffer中。由于currentBuffer引用的是Surface类的成员变量mBuffers的第index个图形缓冲区,因此,前面相当于将图形缓冲区buffer保存在Surface类的成员变量mBuffers的第index个位置中,以便以后可以重复利用。最后,函数还调用GraphicBuffer引用currentBuffer的成员函数setIndex来将前面分配到的图形缓冲区的编号设置为index,这样就可以将它与编号为index的UI元数据缓冲区关联起来。
由于变量s引用的是一个类型为SurfaceLayer的Binder本地对象,因此,接下来我们就继续分析SurfaceLayer类的成员函数requestBuffer的实现,以便可以了解SurfaceFlinger服务是如何为应用程序的一个Surface分配一个图形缓冲区的。
Step 4. SurfaceLayer.requestBuffer
sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, uint32_t w, uint32_t h, uint32_t format, uint32_t usage){ sp<GraphicBuffer> buffer; sp<Layer> owner(getOwner()); if (owner != 0) { /* * requestBuffer() cannot be called from the main thread * as it could cause a dead-lock, since it may have to wait * on conditions updated my the main thread. */ buffer = owner->requestBuffer(index, w, h, format, usage); } return buffer;}这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中。
函数首先调用SurfaceLayer类的成员函数getOwner来获得当前正在处理的一个SurfaceLayer对象的宿主Layer对象,接着再调用这个Layer对象的成员函数requestBuffer来执行分配图形缓冲区的操作。从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道,SurfaceFlinger服务在为Android应用程序创建一个Surface的时候,会相应地创建一个Layer对象和一个SurfaceLayer对象来描述这个Surface。
接下来,我们就继续分析Layer类的成员函数requestBuffer的实现。
Step 5. Layer.requestBuffer
这个函数定义在frameworks/base/services/surfaceflinger/Layer.cpp文件中,我们分段来阅读:
sp<GraphicBuffer> Layer::requestBuffer(int index, uint32_t reqWidth, uint32_t reqHeight, uint32_t reqFormat, uint32_t usage){ sp<GraphicBuffer> buffer; if (int32_t(reqWidth | reqHeight | reqFormat) < 0) return buffer; if ((!reqWidth && reqHeight) || (reqWidth && !reqHeight)) return buffer; // this ensures our client doesn't go away while we're accessing // the shared area. ClientRef::Access sharedClient(mUserClientRef); SharedBufferServer* lcblk(sharedClient.get()); if (!lcblk) { // oops, the client is already gone return buffer; }我们首先明确一下各个函数参数的含义。参数index用来描述一个UI元数据缓冲区的编号,参数reqWidth、reqHeight、reqFormat和usage分别表示要分配的图形缓冲区的宽度、高度、像素格式和用途。函数首先检查各个参数的合法性,即参数reqWidth、reqHeight和reqFormat不能为负数,并且参数reqWidth和reqHeight不能同时等于0。
从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道,Layer类的成员变量mUserClientRef指向了一个ClientRef对象,通过这个ClientRef对象可以获得一个SharedBufferServer对象lcblk。得到的SharedBufferServer对象lcblk就是用来描述正在请求SurfaceFlinger服务分配图形缓冲区的Surface的UI元数据缓冲区堆栈的,接下来我们就会看到它的用法。
我们继续往下看:
/* * This is called from the client's Surface::dequeue(). This can happen * at any time, especially while we're in the middle of using the * buffer 'index' as our front buffer. */ uint32_t w, h, f, bypass; { // scope for the lock Mutex::Autolock _l(mLock); bypass = mBypassState; // zero means default mFixedSize = reqWidth && reqHeight; if (!reqFormat) reqFormat = mFormat; if (!reqWidth) reqWidth = mWidth; if (!reqHeight) reqHeight = mHeight; w = reqWidth; h = reqHeight; f = reqFormat; if ((reqWidth != mReqWidth) || (reqHeight != mReqHeight) || (reqFormat != mReqFormat)) { mReqWidth = reqWidth; mReqHeight = reqHeight; mReqFormat = reqFormat; mNeedsScaling = mWidth != mReqWidth || mHeight != mReqHeight; lcblk->reallocateAllExcept(index); } }这一段代码主要就是用来判断要分配图形缓冲区的Surface的宽度、高度和像素格式是否发生变化。当请求分配的图形缓冲区的宽度、高度和像素格式与这个图形缓冲区所描述的Surface原来的宽度、高度和像素格式不一样时,SurfaceFlinger服务就会认为这个Surface的元信息发生了变化,这时候函数就会将请求分配的图形缓冲区的宽度、高度和像素格式设置为当前Surface的宽度、高度和像素格式,并且调用前面所获得的一个SharedBufferServer对象lcblk的成员函数reallocateAllExcept来将之前已经分配给当前Surface的图形缓冲区设置为无效,因为之前已经分配给当前Surface的图形缓冲区已经不适合于当前Surface使用了。
在这一段代码中,还有一个需要注意的地方,即Layer类的成员变量mBypassState。这个成员变量表示当前正在处理的一个Layer对象所描述的一个Surface在SurfaceFlinger服务渲染UI时,是否需要参与合成。当它的值等于true的时候,就表示不需要参与合成,否则就要参考合成。一般当一个Layer对象所描述的Surface的图形缓冲区是直接在硬件帧缓冲区fb上分配时,对应的Surface就不需要参与SurfaceFlinger服务的合成操作。
我们继续向下看:
// here we have to reallocate a new buffer because the buffer could be // used as the front buffer, or by a client in our process // (eg: status bar), and we can't release the handle under its feet. uint32_t effectiveUsage = getEffectiveUsage(usage); status_t err = NO_MEMORY;#ifdef USE_COMPOSITION_BYPASS if (!mSecure && bypass && (effectiveUsage & GRALLOC_USAGE_HW_RENDER)) { // always allocate a buffer matching the screen size. the size // may be different from (w,h) if the buffer is rotated. const DisplayHardware& hw(graphicPlane(0).displayHardware()); int32_t w = hw.getWidth(); int32_t h = hw.getHeight(); int32_t f = hw.getFormat(); buffer = new GraphicBuffer(w, h, f, effectiveUsage | GRALLOC_USAGE_HW_FB); err = buffer->initCheck(); buffer->transform = uint8_t(getOrientation()); if (err != NO_ERROR) { // allocation didn't succeed, probably because an older bypass // window hasn't released all its resources yet. ClientRef::Access sharedClient(mUserClientRef); SharedBufferServer* lcblk(sharedClient.get()); if (lcblk) { // all buffers need reallocation lcblk->reallocateAll(); } } }#endif这段代码用来判断是否需要直接在硬件帧缓冲区fb上分配一个图形缓冲区。
当满足以下四个条件时,一个图形缓冲区就可以在硬件帧缓冲区fb分配:
1. SurfaceFlinger服务在编译时,定义了USE_COMPOSITION_BYPASS宏;
2. 当前正在处理的Layer对象的成员变量mSecure的值等于false,即这个Layer对象的所描述的一个Surface是非进程间传输安全的,这种类型的Surface一般用来保存屏幕UI数据或者用来传输远程桌面UI数据;
3. 当前正在处理的Layer对象的成员变量mBypassState的值等于true,即这个Layer对象的所描述的一个Surface是不需要参与到SurfaceFlinger服务渲染合成操作的;
4. 请求分配的图形缓冲区的有效用途effectiveUsage的第GRALLOC_USAGE_HW_RENDER位等于1,即请求分配的图形缓冲区要在直接在硬件帧缓冲区上渲染。
当满足上述四个条件,函数就会创建一个类型为GRALLOC_USAGE_HW_FB的图形缓冲区buffer,并且将它返回给应用程序。如果创建失败,函数会调用前面所获得的一个SharedBufferServer对象lcblk的成员函数reallocateAll来将之前已经分配给当前Surface的图形缓冲区都设置为无效,因为这些图形缓冲区有可能不是在硬件帧缓冲区fb上分配的。
请求分配的图形缓冲区的有效用途effectiveUsage是通过调用Surface类的成员函数getEffectiveUsage来获得的,如下所示:
uint32_t Layer::getEffectiveUsage(uint32_t usage) const{ /* * buffers used for software rendering, but h/w composition * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE * * buffers used for h/w rendering and h/w composition * are allocated with HW_RENDER | HW_TEXTURE * * buffers used with h/w rendering and either NPOT or no egl_image_ext * are allocated with SW_READ_RARELY | HW_RENDER * */ if (mSecure) { // secure buffer, don't store it into the GPU usage = GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN; } else { // it's allowed to modify the usage flags here, but generally // the requested flags should be honored. // request EGLImage for all buffers usage |= GraphicBuffer::USAGE_HW_TEXTURE; } return usage;}参数usage用来描述请求分配的图形缓冲区的原始用途。
如果当前正在处理的Layer对象所描述的一个Surface是可以在进程间安全传输的,那么函数就会将参数usage的值修改为(GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN),目的是防止该Surface的图形缓冲区直接在硬件帧缓冲区上分配。
如果当前正在处理的Layer对象所描述的一个Surface是不可以在进程间安全传输的,那么函数除了会保留参数usage的原值之外,还会将它的第GraphicBuffer::USAGE_HW_TEXTURE位设置为1,用来表示分配的图形缓冲区可以用来作为OpenGL库的纹理缓冲区。
最后,函数就将修改后的参数usage的值返回给调用者。
回到Layer类的成员函数requestBuffer中。对于一般应用程序创建的Surface来说,它们都是不可以在进程间安全传输的,即与它对应的Layer对象的成员变量mSecure的值等于false,因此,这时候Layer类的成员函数requestBuffer得到即将要分配的图形缓冲区的有效用途effectiveUsage的GraphicBuffer::USAGE_HW_TEXTURE位就被设置为1。我们记住这个值,以便接下来可以了解图形缓冲区的分配过程。
我们假设SurfaceFlinger服务在编译时,没有定义USE_COMPOSITION_BYPASS宏,或者当前正在处理的Layer对象所描述的一个Surface是需要由SurfaceFlinger服务执行渲染合成操作的,即前面第1个或者第3个条件不满足,于是,我们就继续向下分析Layer类的成员函数requestBuffer的实现:
if (err != NO_ERROR) { buffer = new GraphicBuffer(w, h, f, effectiveUsage); err = buffer->initCheck(); } ..... if (err == NO_ERROR && buffer->handle != 0) { Mutex::Autolock _l(mLock); mBufferManager.attachBuffer(index, buffer); } return buffer;}这段代码首先使用参数w、h、f和effectiveUsage来创建了一个GraphicBuffer对象buffer,用来描述即将要分配的图形缓冲区,接着再调用Layer类的成员变量mBufferManager所描述的一个BufferManager对象的成员函数attachBuffer来将GraphicBuffer对象buffer的编号设置为index,以便可以表示这个GraphicBuffer对象buffer是为编号为index的UI元数据缓冲区创建的。
最后,Layer类的成员函数requestBuffer就将分配好的图形缓冲区,即GraphicBuffer对象buffer返回给应用程序。
接下来,我们首先分析一个GraphicBuffer对象的创建过程,即GraphicBuffer类的构造函数的实现,以便可以了解它所描述的图形缓冲区是如何分配的,接着再分析BufferManager类的成员函数attachBuffer的实现,以便可以了解SurfaceFlinger服务是如何将一个UI元数据缓冲区与一个图形缓冲区关联起来的。
Step 6. new GraphicBuffer
GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, PixelFormat reqFormat, uint32_t reqUsage) : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), mInitCheck(NO_ERROR), mIndex(-1){ width = height = stride = format = usage = transform = 0; handle = NULL; mInitCheck = initSize(w, h, reqFormat, reqUsage);}
这个函数定义在文件frameworks/base/libs/ui/GraphicBuffer.cpp文件中。
GraphicBuffer类的构造函数最重要的是调用另外一个成员函数initSize来初始化即将要分配的图形缓冲区。
Step 7. GraphicBuffer.initSize
status_t GraphicBuffer::initSize(uint32_t w, uint32_t h, PixelFormat format, uint32_t reqUsage){ GraphicBufferAllocator& allocator = GraphicBufferAllocator::get(); status_t err = allocator.alloc(w, h, format, reqUsage, &handle, &stride); if (err == NO_ERROR) { this->width = w; this->height = h; this->format = format; this->usage = reqUsage; } return err;}这个函数定义在文件frameworks/base/libs/ui/GraphicBuffer.cpp文件中。
函数首先获得一个GraphicBufferAllocator对象,然后再调用这个GraphicBufferAllocator对象的成员函数alloc来分配一块指定大小、像素格式以及用用途的图形缓冲区。分配好的图形缓冲的句柄值最终就保存在GraphicBuffer类的成员变量handle中。
GraphicBufferAllocator类是用来分配图形缓冲区的,接下来我们就继续分析它的成员函数alloc的实现。
Step 8. GraphicBufferAllocator.alloc
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, buffer_handle_t* handle, int32_t* stride){ // make sure to not allocate a N x 0 or 0 x N buffer, since this is // allowed from an API stand-point allocate a 1x1 buffer instead. if (!w || !h) w = h = 1; // we have a h/w allocator and h/w buffer is requested status_t err; if (usage & GRALLOC_USAGE_HW_MASK) { err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); } else { err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride); } ...... return err;}这个函数定义在文件frameworks/base/libs/ui/GraphicBufferAllocator.cpp中。
参数usage是从前面的Step 5中传进来的,前面我们假设它的第GraphicBuffer::USAGE_HW_TEXTURE位的值等于1。
GraphicBuffer::USAGE_HW_TEXTURE是一个枚举值,定义在文件frameworks/base/include/ui/GraphicBuffer.h中,如下所示:
class GraphicBuffer : public EGLNativeBase< android_native_buffer_t, GraphicBuffer, LightRefBase<GraphicBuffer> >, public Flattenable{public: enum { ...... USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE, ...... }; ......};它的值等于GRALLOC_USAGE_HW_TEXTURE。
GRALLOC_USAGE_HW_TEXTURE和GRALLOC_USAGE_HW_MASK也是两个枚举值,它们定义在文件hardware/libhardware/include/hardware/gralloc.h中,如下所示:
enum { ...... /* buffer will be used as an OpenGL ES texture */ GRALLOC_USAGE_HW_TEXTURE = 0x00000100, ...... /* mask for the software usage bit-mask */ GRALLOC_USAGE_HW_MASK = 0x00001F00, ......};从GRALLOC_USAGE_HW_TEXTURE和GRALLOC_USAGE_HW_MASK这两个枚举值的定义可以知道,GraphicBufferAllocator类的成员函数alloc最终会调用其成员变量mAllocDev的成员函数alloc来分配一个图形缓冲区。
GraphicBufferAllocator类的成员变量mAllocDev指向了一个alloc_device_t结构体,用来描述HAL层的Gralloc模块中的一个gralloc设备,这个gralloc设备是在GraphicBufferAllocator类的构造函数中创建的,如下所示:
GraphicBufferAllocator::GraphicBufferAllocator() : mAllocDev(0){ hw_module_t const* module; int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); if (err == 0) { gralloc_open(module, &mAllocDev); }}GraphicBufferAllocator类的构造函数首先调用函数hw_get_module来加载ID值等于GRALLOC_HARDWARE_MODULE_ID的HAL模块,即加载HAL层中的Gralloc模块,目的是为了接下来调用函数gralloc_open来打开里面的gralloc设备,并且将这个打开的gralloc设备保存在GraphicBufferAllocator类的成员变量mAllocDev中,这一点可以参考前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文。
接下来,我们就继续分析alloc_device_t结构体的成员函数alloc的实现。
Step 9. alloc_device_t.alloc
从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,Gralloc模块中的gralloc设备的成员函数alloc被设置为Gralloc模块中的函数gralloc_alloc。
Gralloc模块模块中的函数gralloc_alloc有一个参数usage,当它的GRALLOC_USAGE_HW_FB位等于1的时候,函数gralloc_alloc就会直接在硬件帧缓冲区上分配一个图形缓冲区,否则的话,就会在匿名共享内存中分配一个图形缓冲区。从前面的调用过程可以知道,这个参数的值最开始是从前面的Step 1传过来的,即是从应用程序进程这一侧传递过来的。
由于整个系统在硬件上就只有一个帧缓冲区,它是由SurfaceFlinger服务来统一管理的,即只有SurfaceFlinger服务使用的图形缓冲区才可以在上面分配,否则的话,随便一个应用程序进程都可以在上面分配图形缓冲区来使用,这个帧缓冲区的管理就乱套了。应用程序进程使用的图形缓冲区一般都是在匿名共享内存里面分配的,这个图形缓区填好数据之后,就会再交给SurfaceFlinger服务来合成到硬件帧缓冲区上去渲染。因此,从前面Step 1传过来给函数gralloc_alloc的参数usage的GRALLOC_USAGE_HW_FB位会被设置为0,以便可以在匿名共享内存中分配一个图形缓冲区。这个分配的过程可以参考前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文,这里就不再复述了。
这一步执行完成之后,应用程序所请求的图形缓冲区就分配完成了,回到前面的Step 5中,即Layer类的成员函数requestBuffer中,接下来就会调用BufferManager类的成员函数attachBuffer来设置这个图形缓冲区的编号,以便它可以与一个UI元数据缓冲区关联起来的。关联好之后,应用程序在请求SurfaceFlinger服务渲染一个Surface时,只需要指定一个UI元数据缓冲区的编号,SurfaceFlinger服务就可以根据这个编号来找到对应的图形缓冲区,进而把这个图形缓冲区的内容渲染到硬件帧缓冲区上去,即渲染到设备显示屏上去。
接下来,我们就继续分BufferManager类的成员函数attachBuffer的实现。
Step 10. BufferManager.attachBuffer
status_t Layer::BufferManager::attachBuffer(size_t index, const sp<GraphicBuffer>& buffer){ BufferData* const buffers = mBufferData; Mutex::Autolock _l(mLock); buffers[index].buffer = buffer; buffers[index].texture.dirty = true; return NO_ERROR;}这个函数定义在frameworks/base/services/surfaceflinger/Layer.cpp文件中。
从前面的调用过程可以知道,参数index用来描述一个UI元数据缓冲区的编号,而参数buffer用来描述一个图形缓冲区。
BufferManager类的成员变量mBufferData是一个类型为BufferData的数组,这个数组就是用来关联当前正在处理的一个Layer对象所描述的一个Surface的UI元数据缓冲区和图形缓冲区的。例如,编号为i的UI元数据缓冲区在数组mBufferData的第i个位置有一个对应的BufferData结构体,而这个BufferData结构体的成员变量buffer就指向为编号为i的UI元数据缓冲区所分配的一个图形缓冲区。
理解了这一点之后,我们就不难理解BufferManager类的成员函数attachBuffer的实现了,它就是将编号为index的UI元数据缓冲区与参数buffer所描述的图形缓冲区关联起来。
这一步执行完成之后,回到前面的Step 5中,即Layer类的成员函数requestBuffer中,接下来就会将分配好的图形缓冲区返回给应用程序,即返回到前面的Step 3中去,这时候应用程序就继续调用GraphicBufferMapper类的成员函数registerBuffer来将获得的图形缓冲区注册到当前进程的地址空间去。
Step 11. GraphicBufferMapper.registerBuffer
status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle){ status_t err; if (sw_gralloc_handle_t::validate(handle) < 0) { err = mAllocMod->registerBuffer(mAllocMod, handle); } else { err = sw_gralloc_handle_t::registerBuffer((sw_gralloc_handle_t*)handle); } LOGW_IF(err, "registerBuffer(%p) failed %d (%s)", handle, err, strerror(-err)); return err;}这个函数定义在文件frameworks/base/libs/ui/GraphicBufferMapper.cpp中。
参数handle的类型为buffer_handle_t,它描述的便是前面在匿名共享内存中分配的一个图形缓冲区。
从前面的Step 8可以知道,当GraphicBufferAllocator类的成员函数alloc的参数usage的第GraphicBuffer::USAGE_HW_TEXTURE位的值等于1的时候,SurfaceFlinger服务就会通过HAL层的Gralloc模块来分配一个图形缓冲区,否则的话,就会在sw_gralloc_handle_t模块中分配一个图形缓冲区。sw_gralloc_handle_t模块中分配的图形缓冲区是使用一个sw_gralloc_handle_t对象来描述的,而从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,Gralloc模块分配的图形缓冲区是使用一个private_handle_t对象来描述的。
sw_gralloc_handle_t类的静态成员函数validate就是用来验证参数handle所描述的一个图形缓冲区是否是在sw_gralloc_handle_t模块中分配的。如果是的话,那么它的返回值就会等于0,否则的话,就会小于0。
由于参数handle所描述的一个图形缓冲区是在Gralloc模块中分配的,因此,这个函数接下来就会调用GraphicBufferAllocator类的成员变量mAllocMod的成员函数registerBuffer来执行注册图形缓冲区的操作。
GraphicBufferAllocator类的成员变量mAllocMod指向了HAL层中的Gralloc模块,它是在GraphicBufferAllocator类的构造函数中初始化的,如下所示:
GraphicBufferMapper::GraphicBufferMapper() : mAllocMod(0){ hw_module_t const* module; int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); if (err == 0) { mAllocMod = (gralloc_module_t const *)module; }}GraphicBufferAllocator类的构造函数首先调用函数hw_get_module来加载ID值等于GRALLOC_HARDWARE_MODULE_ID的模块,就可以将Gralloc模块加载到应用程序进程中来。从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,Gralloc模块是使用一个gralloc_module_t结构体来描述的,因此,GraphicBufferAllocator类的构造函数最终就可以将加载得到的模块module转换为一个gralloc_module_t结构体,并且保存在GraphicBufferAllocator类的成员变量mAllocMod中。
接下来,我们就继续分析gralloc_module_t结构体的成员函数registerBuffer的实现。
Step 12. gralloc_module_t.registerBuffer
从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,Gralloc模块的成员函数registerBuffer被设置为Gralloc模块中的函数gralloc_register_buffer。Gralloc模块模块中的函数gralloc_register_buffer主要就是将一块指定的图形缓冲区映射到当前进程的地址空间来。在我们这个情景中,就是映射到应用程序进程的地址空间来。
这一步执行完成之后,应用程序请求SurfaceFlinger服务为一个空闲的UI元数据缓冲区分配一个图形缓冲区的过程就分析完成了。从分析的过程可以知道,这个图形缓冲区指向的是一块匿名共享内存,最初是在SurfaceFlinger服务这一侧分配的,然后再返回给应用程序进程这一侧,并且会被映射到应用程序进程的地址空间来。这样,SurfaceFlinger服务和应用程序就可以在各自的地址空间中访问这个图形缓冲区,其中,应用程序访问这个图形缓冲区的目的是往里面写入UI数据,而SurfaceFlinger服务访问这个图形缓冲区的目的是将里面的UI数据读取出来渲染。
接下来,我们继续再分析Surface类的静态成员函数queueBuffer的实现,以便可以了解应用程序是如何将一块已经填好了数据的UI元数据缓冲区添加到当前正在绘制的Surface的UI元数缓冲区堆栈的待渲染队列中去,这个过程如图3所示:
这个过程一共分为4个步骤,接下来我们就详细分析每一个步骤。
Step 1. Surface.queueBuffer
int Surface::queueBuffer(ANativeWindow* window, android_native_buffer_t* buffer) { Surface* self = getSelf(window); return self->queueBuffer(buffer);}这个函数定义文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
参数window虽然是一个ANativeWindow指针,但是它实际上指向的是一个Surface对象,因此,函数首先调用另外一个静态成员函数getSelf来将它转换为一个Surface对象self,接着再调用这个Surface对象self的成员函数queueBuffer将一个UI元数据缓冲区加入到待渲染队列中去,其中,要加入到待渲染队列中去的UI元数据缓冲区就是使用参数buffer来描述的。
Surface类的非静态成员函数queueBuffer的实现如下所示:
int Surface::queueBuffer(android_native_buffer_t* buffer){ status_t err = validate(); if (err != NO_ERROR) return err; if (mSwapRectangle.isValid()) { mDirtyRegion.set(mSwapRectangle); } int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer)); GraphicLog::getInstance().log(GraphicLog::SF_APP_QUEUE, mIdentity, bufIdx); mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform); mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop); mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion); err = mSharedBufferClient->queue(bufIdx); LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err)); if (err == NO_ERROR) { // TODO: can we avoid this IPC if we know there is one pending? mClient.signalServer(); } return err;}这个函数定义文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
Surface类的成员变量mSwapRectangle用来指定要绘制的Surface的区域。如果它的值是正确的,那么函数就会将它的值设置到Surface类的另外一个成员变量mDirtyRegion中去,以便接下来可以用来作为裁剪区域传递给SurfaceFlinger服务。
如前所述,参数buffer描述的是一个图形缓冲区,它的实际类型为GraphicBuffer,因此,函数就可以调用GraphicBuffer的静态成员函数getSelf来将它转换为一个GraphicBuffer对象,接着函数还调用Sufrace类的成员函数getBufferIndex来获得参数buffer所描述的一个图形缓冲区的编号bufIdx。这个编号是用来关联一个UI元数据缓冲区。在前面分析空闲UI元数据缓冲区及其图形缓冲区的分配过程的Step 3中提到,应用程序请求SurfaceFlinger服务为一个UI元数据缓冲区分配了一个图形缓冲区时,就会将这个UI元数据缓冲区的编号设置到这个图形缓冲区里面去。
Surface类的成员变量mSharedBufferClient指向了一个SharedBufferClient对象,用来描述当前正在绘制的Surface的UI元数据缓冲区堆栈,接下来函数就会调用它的成员函数queue来将前面获得的一个编号为bufIdx的UI元数据缓冲区加入到它的待渲染队列中去。不过,在加入这个编号为bufIdx的UI元数据缓冲区之前,函数还会分别调用成员变量mSharedBufferClient的成员函数setTransform、setCrop和setDirtyRegion来设置它的旋转方向、纹理坐标和裁剪区域等信息。这些信息分别保存在Surface类的成员变量mNextBufferTransform、mNextBufferCrop和mDirtyRegion中。注意,Surface类的成员变量mNextBufferTransform和mNextBufferCrop是由OpenGL库通过调用Surface类的成员函数perform来设置。从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道,应用程序在请求SurfaceFlinger服务创建一个绘图表面的时候,会将用来描述这个绘图表面的一个Surface对象的成员函数perform设置为OpenGL库在画图的过程需要调用到的一个回调接口peform,这样做的目的就是为了可以设置绘图表面的元信息。
将编号为bufIdx的UI元数据缓冲区加入到正在绘制的Surface的UI元数据缓冲区堆栈的待渲染队列之后,函数最后就会调用Surface类的成员变量mClient所描述的一个SurfaceClient对象的成员函数signalServer来通知SurfaceFlinger服务来相应的图形缓冲区渲染到设备显示屏上去。
接下来,我们首先分析SharedBufferClient类的成员函数queue的实现,以便可以了解一个UI元数缓冲区是如何进入到一个UI元数据缓冲区堆栈的待渲染队列中去的,接着分析SurfaceClient类的成员函数signalServer的实现,以便可以了解应用程序是如何请求SurfaceFlinger服务渲染一个Surface的图形缓冲区的。
Step 2. SharedBufferClient.queue
status_t SharedBufferClient::queue(int buf){ RWLock::AutoRLock _rd(mLock); SharedBufferStack& stack( *mSharedStack ); queued_head = (queued_head + 1) % mNumBuffers; stack.index[queued_head] = buf; QueueUpdate update(this); status_t err = updateCondition( update ); LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string()); const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); return err;}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
SharedBufferClient类的成员变量mSharedStack是从父类SharedBufferBase继承下来的,它的成员变量index是一个类型为int8_t的数组,而这个数组就是用来描述一个UI元数据缓冲区堆栈的,这一点可以参考前面Android应用程序与SurfaceFlinger服务之间的共享UI元数据(SharedClient)的创建过程分析一文。
从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文可以知道,SharedBufferClient类的成员变量queue_head指向了一个UI元数据缓冲区堆栈的待渲染队列的尾部,所有需要加入到这个待渲染队列的UI元数据缓冲都保存在queue_head的下一个位置上。
参数buf描述的是要加入到当前正在绘制的Surface的UI元数据缓冲区堆栈的待渲染队列的缓冲区的编号,在将这个缓冲区加入到待渲染队列之后,还需要将这个待渲染队列的大小增加1,以便SurfaceFlinger服务可以知道一个Surface当前有多少个图形缓冲区是正在等待被渲染的。从前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文可以知道,一个UI元数据缓冲区堆栈的待渲染队列的大小保存在一个SharedBufferStack对象的成员变量queued中,而将这个待渲染队列的大小增加1的操作是通过调用用SharedBufferClient类从SharedBufferBase类继承下来的成员函数updateCondition来实现的。
函数首先创建了一个QueueUpdate对象update,然后再以这个QueueUpdate对象update为参数,来调用SharedBufferClient从SharedBufferBase类继承下来的成员函数updateCondition,就可以将当前正在处理的一个UI元数据缓冲区堆栈的待渲染队列的大小增加1了。
这一步执行完成之后,就返回到Step 1中,即Surface类的成员函数queueBuffer中,这时候与需要渲染的图形缓冲区所对应的UI元数据缓冲区就加入到当前正在绘制的Surface的UI元数据缓冲区的待渲染队列中去了,接下来,应用程序就会调用SurfaceClient类的成员函数signalServer来请求SurfaceFlinger服务渲染这个Surface。
Step 3. SurfaceClient.signalServer
class SurfaceClient : public Singleton<SurfaceClient>{ // all these attributes are constants sp<ISurfaceComposer> mComposerService; ......public: ...... void signalServer() const { mComposerService->signal(); }};这个函数定义在文件frameworks/base/libs/surfaceflinger_client/Surface.cpp中。
从前面Android应用程序与SurfaceFlinger服务之间的共享UI元数据(SharedClient)的创建过程分析一文可以知道,SurfaceClient类的成员变量mComposerService指向了一个实现了ISurfaceComposer接口的Binder代理对象,而这个Binder代理对象引用了系统中的SurfaceFlinger服务,因此,SurfaceClient类的成员函数signalServer实际上就是通过成员变量mComposerService的成员函数signal来通知SurfaceFlinger服务执行渲染Surface的操作。
这一步执行完成之后,就会导致SurfaceFlinger类的成员函数signal被调用,接下来我们继续分析这个成员函数的实现。
Step 4. SurfaceFlinger.signal
void SurfaceFlinger::signal() const { // this is the IPC call const_cast<SurfaceFlinger*>(this)->signalEvent();}这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
SurfaceFlinger服务是运行在System进程中的一个单独的线程中的。当SurfaceFlinger服务什么也不需要做的时候,它就会在这个线程中睡眠。由于现在有应用程序请求SurfaceFlinger服务执行渲染Surface的操作了,因此,就需要将这个线程唤醒起来了。
唤醒SurfaceFlinger服务所运行在的线程的操作是通过调用SurfaceFlinger类的成员函数signalEvent来实现的。当这个线程被唤醒之后,它就会继续执行SurfaceFlinger类的成员函数threadLoop来执行渲染Surface的操作。在后面的文章中,我们再详细分析SurfaceFlinger服务的实现,到时候就可以知道SurfaceFlinger服务的运行原理了。
接下来,我们就从SurfaceFlinger类的成员函数threadLoop开始,分析SurfaceFlinger服务渲染Surface的图形缓冲区的过程。这里我们并不打算详细分析这个过程,而是粗略地分析,目的是为了理清一下思路,为后面从正面分析SurfaceFlinger服务的实现打下基础。这个过程如图4所示。
图 4 SurfaceFlinger服务渲染Surface的过程
这个过程一共分为6个步骤,接下来我们就详细分析每一个步骤。
Step 1. SurfaceFlinger.threadLoop
bool SurfaceFlinger::threadLoop(){ waitForEvent(); ...... // post surfaces (if needed) handlePageFlip(); const DisplayHardware& hw(graphicPlane(0).displayHardware()); if (LIKELY(hw.canDraw() && !isFrozen())) { ...... // repaint the framebuffer (if needed) const int index = hw.getCurrentBufferIndex(); GraphicLog& logger(GraphicLog::getInstance()); logger.log(GraphicLog::SF_REPAINT, index); handleRepaint(); // inform the h/w that we're done compositing logger.log(GraphicLog::SF_COMPOSITION_COMPLETE, index); hw.compositionComplete(); logger.log(GraphicLog::SF_SWAP_BUFFERS, index); postFramebuffer(); logger.log(GraphicLog::SF_REPAINT_DONE, index); } else { // pretend we did the post hw.compositionComplete(); usleep(16667); // 60 fps period } return true;}这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
在SurfaceFlinger服务中,每一个Surface,或者说每一个Layer,都有自己的一系列图形缓冲区。而对于每一个Surface来说,它们各自需要渲染的图形缓冲区都保存在内部的一个UI元数据缓冲区堆栈的待渲染队列中。当SurfaceFlinger服务需要渲染系统的UI时,首先就会将各个Surface的UI元数据缓冲区堆栈的待渲染队列的缓冲区逐个取出来,并且找到对应的图形缓冲区,接着再将这些图形缓冲区合成在一起渲染到硬件帧缓冲区fb上,最后我们就可以在设备显示屏上看到系统的UI了。
了解了这个背景知识之后,接下来我们就简要描述SurfaceFlinger类的成员函数threadLoop的工作过程:
1. 在SurfaceFlinger类的成员函数waitForEvent中等待。一旦SurfaceFlinger类的成员函数signalEvent被调用,那么SurfaceFlinger服务所运行的线程就会被唤醒。
2. 调用SurfaceFlinger类的成员函数handlePageFlip将各个Surface的UI元数据缓冲区堆栈的待渲染队列头部的缓冲区取出来,并且找到对应的图形缓冲区。这些图缓冲区就是接下来要被合成和渲染的。
3. 调用SurfaceFlinger类的成员函数handleRepaint来合成第2步得到的图形缓冲区。
4. SurfaceFlinger服务使用一个DisplayHardware对象hw来描述系统当前所使用的显示屏,这个DisplayHardware对象hw实际就是用来封装对硬件帧缓冲区fb的访问的,第3步就是将各个图形缓冲区的内容合成到这个DisplayHardware对象hw中去。合成完成之后,就会调用这个DisplayHardware对象hw的成员函数compositionComplete来通知HAL层Gralloc模块中的fb设备。这一步是可选的,即HAL层Gralloc模块中的fb设备可以忽略掉这个合成完成通知。
5. 调用SurfaceFlinger类的成员函数postFramebuffer来将产须合成好的图形缓冲区渲染到硬件帧缓冲区fb上去。
这里我们只关注第2步的实现,以后分析SurfaceFlinger服务的实现时,再分析其它步骤的实现。
Step 2. SurfaceFlinger.handlePageFlip
void SurfaceFlinger::handlePageFlip(){ bool visibleRegions = mVisibleRegionsDirty; LayerVector& currentLayers = const_cast<LayerVector&>( mDrawingState.layersSortedByZ); visibleRegions |= lockPageFlip(currentLayers); const DisplayHardware& hw = graphicPlane(0).displayHardware(); const Region screenRegion(hw.bounds()); if (visibleRegions) { Region opaqueRegion; computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion); /* * rebuild the visible layer list */ mVisibleLayersSortedByZ.clear(); const LayerVector& currentLayers(mDrawingState.layersSortedByZ); size_t count = currentLayers.size(); mVisibleLayersSortedByZ.setCapacity(count); for (size_t i=0 ; i<count ; i++) { if (!currentLayers[i]->visibleRegionScreen.isEmpty()) mVisibleLayersSortedByZ.add(currentLayers[i]); } ...... mWormholeRegion = screenRegion.subtract(opaqueRegion); mVisibleRegionsDirty = false; } unlockPageFlip(currentLayers); mDirtyRegion.andSelf(screenRegion);}
这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
SurfaceFlinger服务将系统中的各个Surface按照其Z轴大小排列在SurfaceFlinger类的成员变量mDrawingState内部的一个layersSortedByZ列表中。函数首先将这个Surface列表layersSortedByZ取出来,并且调用SurfaceFlinger类的另外一个成员函数lockPageFlip来将各个Surface当前需要渲染的图形缓冲区取出来。
如果SurfaceFlinger服务确实需要执行渲染图形缓冲区的操作,那么SurfaceFlinger类的成员函数lockPageFlip的返回值就会等于true。在这种情况下,函数接下来主要就是调用SurfaceFlinger类的成员函数computeVisibleRegions来计算需要渲染的各个Surface的可见区域,以便后面可以正确地将它们的UI绘制出来。
接下来,我们只关注SurfaceFlinger类的成员函数lockPageFlip的实现,以便可以了解SurfaceFlinger服务是如何将各个Surface当前需要渲染的图形缓冲区取出来的。
Step 3. SurfaceFlinger.lockPageFlip
bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers){ bool recomputeVisibleRegions = false; size_t count = currentLayers.size(); sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { const sp<LayerBase>& layer(layers[i]); layer->lockPageFlip(recomputeVisibleRegions); } return recomputeVisibleRegions;}这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。
保存在Surface列表currentLayers中的Surface有可能是一个Layer对象,也有可能一个是LayerBlur对象或者一个LayerDim对象等。这里我们只关心类型为Layer的Surface的图形缓冲区是如何取出来渲染的,这是通过调用Layer类的成员函数lockPageFlip来实现的。
Step 4. Layer.lockPageFlip
void Layer::lockPageFlip(bool& recomputeVisibleRegions){ ClientRef::Access sharedClient(mUserClientRef); SharedBufferServer* lcblk(sharedClient.get()); ...... ssize_t buf = lcblk->retireAndLock(); ...... // we retired a buffer, which becomes the new front buffer if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { ...... return; } sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); if (newFrontBuffer != NULL) { // get the dirty region // compute the posted region const Region dirty(lcblk->getDirtyRegion(buf)); mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() ); ...... // get the crop region setBufferCrop( lcblk->getCrop(buf) ); // get the transformation setBufferTransform( lcblk->getTransform(buf) ); } ...... if (lcblk->getQueuedCount()) { // signal an event if we have more buffers waiting mFlinger->signalEvent(); } ......}
这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中。
从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道,Layer类的成员变量mUserClientRef指向了一个ClientRef对象,通过这个ClientRef对象可以获得一个SharedBufferServer对象lcblk。有了SharedBufferServer对象lcblk之后,函数就可以调用它的成员函数retireAndLock来从它所描述的UI元数据缓冲区堆栈的待渲染队列中取出一个缓冲区来,以便接下来可以找到对应的图形缓冲区来渲染。
注意,调用SharedBufferServer对象lcblk的成员函数retireAndLock得到的是一个UI元数据缓冲区的编号,这个编号保存在变量buf中。函数接下来就会调用Layer类的成员变量mBufferManager所描述的一个BufferManager对象的成员函数setActiveBufferIndex来将这个编号为buf的图形缓冲区设置当前激活的图形缓冲区,即接下来要被SurfaceFlinger服务渲染的图形缓冲区。前面在分析空闲UI元数据缓冲区及其图形缓冲区的分配过程的Step 10时提到,每一个分配的图形缓冲区都关联有一个编号,而这个编号正好就是一个对应的UI元数据缓冲区的编号。因此,知道了一个UI元数据缓冲区的编号之后,就可以找到对应的图形缓冲区。
函数接下来还会通过SharedBufferServer对象lcblk的成员函数getDirtyRegion、getCrop和getTransform来获得当前激活的图形缓冲区的元信息,即裁剪区域、纹理坐标和旋转方向。这些元信息是在前面分析UI元数据缓冲区进入待渲染队列的过程的Step 1中提供的,因此,这里就可以将它们获取回来。后面在渲染当前激活的图形缓冲区时,就需要使用到这些元信息。
最后,函数调用SharedBufferServer对象lcblk的成员函数getQueueCount来检查待渲染待队列中的缓冲区的个数是否大于0。如果大于0,那么就说明还有图形缓冲区在等待被渲染。在这种情况下,函数就就会继续调用Layer类的成员变量mFlinger的成员函数signalEvent来通知SurfaceFlinger服务在执行完成当前这次的Surface渲染操作之后,接着要马上进行下一次的Surface渲染操作。
接下来,我们首先分析SharedBufferServer类的成员函数retireAndLock的实现,以便可以了解SurfaceFlinger服务是如何从一个Surface的UI元数据缓冲区堆栈的待渲染队列中取出一个缓冲区编号的,接着再分析BufferManager类的成员函数setActiveBufferIndex的实现,以便可以了解一个图形缓冲区是如何被设置为一个Surface的当前激活的图形缓冲区的。
Step 5. SharedBufferServer.retireAndLock
ssize_t SharedBufferServer::retireAndLock(){ RWLock::AutoRLock _l(mLock); RetireUpdate update(this, mNumBuffers); ssize_t buf = updateCondition( update ); if (buf >= 0) { if (uint32_t(buf) >= SharedBufferStack::NUM_BUFFER_MAX) return BAD_VALUE; SharedBufferStack& stack( *mSharedStack ); buf = stack.index[buf]; LOGD_IF(DEBUG_ATOMICS && buf>=0, "retire=%d, %s", int(buf), dump("").string()); } return buf;}这个函数定义在文件frameworks/base/libs/surfaceflinger_client/SharedBufferStack.cpp中。
函数首先创建了一个RetireUpdate对象update,然后再调用SharedBufferServer从SharedBufferBase类继承下来的成员函数updateCondition来获得当前正在使用的UI元数据缓冲区堆栈的待渲染队列头部的缓冲区在堆栈中的位置值buf。有了这个位置值buf之后,函数再接下来就可以从用来描述当前正在使用的UI元数据缓冲区堆栈的一个index数组中获得一个对应的缓冲区的编号。这个编号即为待渲染队列头部的UI元数据缓冲区的编号,因此,函数最后就可以将它返回给调用者。
这一步执行完成之后,就返回到Step 4中,即Layer类的成员函数lockPageFlip中,这时候SurfaceFlinger服务就可以就为当前正在处理的Surface设置当前激活的图形缓冲区了,这是通过调用BufferManager类的成员函数setActiveBufferIndex来实现的。
Step 6. BufferManager.setActiveBufferIndex
status_t Layer::BufferManager::setActiveBufferIndex(size_t index) { mActiveBuffer = index; return NO_ERROR;}这个函数定义在文件frameworks/base/services/surfaceflinger/Layer.cpp中。
BufferManager类的成员变量mActiveBuffer用来描述一个Surface的当前激活的图形缓冲区的编号。由于参数index正好就是描述一个Surface的当前激活的图形缓冲区的编号,因此,函数就可以将它保存在BufferManager类的成员变量mActiveBuffer中。
前面在分析空闲UI元数据缓冲区及其图形缓冲区的分配过程的Step 10时提到,BufferManager类有一个类型BufferData数组的成员变量mBufferData,它里面保存了SurfaceFlinger服务为一个Surface所分配的图形缓冲区,这样,后面SurfaceFlinger服务在渲染一个Surface时,就可以通过与它所关联的一个BufferManager对象的成员变量mActiveBuffer来在另外一个成员变量mBufferData中找到当前激活的图形缓冲区。
这一步执行完成之后,SurfaceFlinger服务渲染Surface的图形缓冲区的过程就分析完成了。这个过程虽然只是一个粗略的过程,但是已经足够我们理解Android应用程序请求SurfaceFlinger服务渲染Surface的过程了,同时也为后面我们从正面分析SurfaceFlinger服务的实现原理理清了思路,以及打下了良好的基础。
至此,Android应用程序和SurfaceFlinger服务的关系我们就学习完成了。要重新学习,请参考前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划一文。接下来,我们还会继续分析SurfaceFlinger服务的实现原理,敬请关注!
- 4楼leihengxin5小时前
- 顶一下。
- 3楼www_k2tiyu_com昨天 22:11
- 整理的比较完善。。。学习了
- 2楼chinanick028昨天 19:39
- 很好。很好。
- 1楼JuneIvan昨天 15:56
- 书啥时候出啊?等不及了
- Re: Luoshengyang昨天 16:25
- 回复JuneIvann快了,下个月的样子可以出来。
- Re: JuneIvan昨天 16:43
- 回复Luoshengyangn几本?比这个博客有删减吗?到时一定支持
- Re: Luoshengyang昨天 19:20
- 回复JuneIvann一本,出版的书没有UI这部分内容,目录可以看一下这里:http://blog.csdn.net/luoshengyang/article/details/7409491
- Re: JuneIvan昨天 19:24
- 回复Luoshengyangn应该能涵盖除了UI之外的所有博客吧?n另,老罗,UI,audio,connectivity,还有dalvik这些以后会有分析吗?
- Re: Luoshengyang昨天 19:33
- 回复JuneIvann嗯,除了UI部分,都涵盖了,而且会被博客的内容会讲得更详细。UI这部分内容会一直坚持分析下去,之后有计划再分析dalvik。audio和connectivity这些针对性很强的话题可能就不会分析了,博客的宗旨是学习Android系统的基础架构,偏向分析比较通用的话题。