Android应用程序请求SurfaceFlinger服务渲染Surface的过程分析

发表于 5年以前  | 总阅读数:3059 次

在前面一篇文章中,我们分析了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指针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元数据缓冲区;

  1. 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数据;

  1. 当前正在处理的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模块中分配的,因此,这个函数接下来就会调用GraphicBufferMapper类的成员变量mAllocMod的成员函数registerBuffer来执行注册图形缓冲区的操作。

GraphicBufferMapper类的成员变量mAllocMod指向了HAL层中的Gralloc模块,它是在GraphicBufferMapper类的构造函数中初始化的,如下所示:

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;
        }
    }

GraphicBufferMapper类的构造函数首先调用函数hw_get_module来加载ID值等于GRALLOC_HARDWARE_MODULE_ID的模块,就可以将Gralloc模块加载到应用程序进程中来。从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,Gralloc模块是使用一个gralloc_module_t结构体来描述的,因此,GraphicBufferMapper类的构造函数最终就可以将加载得到的模块module转换为一个gralloc_module_t结构体,并且保存在GraphicBufferMapper类的成员变量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所示:

图3 准备就绪的UI元数据缓冲区进入待渲染队列的过程

这个过程一共分为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对象,接着函数还调用Surface类的成员函数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服务的实现原理,敬请关注!

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录