Android帧缓冲区状态监控过程源码分析

https://www.2cto.com/kf/201312/261730.html

 

SurfaceFlinger服务在启动的时候,会创建一个线程来监控由内核发出的帧缓冲区硬件事件。每当帧缓冲区要进入睡眠状态时,内核就会发出一个睡眠事件,这时候SurfaceFlinger服务就会执行一个释放屏幕的操作;而当帧缓冲区从睡眠状态唤醒时,内核就会发出一个唤醒事件,这时候SurfaceFlinger服务就会执行一个获取屏幕的操作。

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

status_t SurfaceFlinger::readyToRun()

{

    ALOGI( "SurfaceFlinger's main thread ready to run. "

            "Initializing graphics H/W...");

    // we only support one display currently

    int dpy = 0;

    {

        // initialize the main display

        GraphicPlane& plane(graphicPlane(dpy));

        DisplayHardware* const hw = new DisplayHardware(this, dpy);

        plane.setDisplayHardware(hw);

    }

    ...

    //启动显示屏睡眠/唤醒状态监控

    hw.startSleepManagement();

    ...

}

在SurfaceFlinger启动过程中,将创建一个DisplayHardware对象,在构造DisplayHardware的父类DisplayHardwareBase对象时,会创建一个线程,用来监控硬件帧缓冲区的睡眠和唤醒事件。我们知道,在构造一个子类对象时,首先会调用父类的构造函数,因此在构造DisplayHardware对象时,首先会执行DisplayHardwareBase的构造函数:

 

 

?

1

2

3

4

5

6

7

DisplayHardwareBase::DisplayHardwareBase(const sp<surfaceflinger>& flinger,

        uint32_t displayIndex)

{

    mScreenAcquired = true;

    //创建一个帧缓存区状态监控线程

    mDisplayEventThread = new DisplayEventThread(flinger);

}</surfaceflinger>

DisplayEventThread线程用于监控硬件帧缓存区的状态,其构造函数如下:

 

 

?

1

2

3

4

DisplayHardwareBase::DisplayEventThread::DisplayEventThread(

        const sp<surfaceflinger>& flinger)

    : Thread(false), mFlinger(flinger) {

}</surfaceflinger>

当SurfaceFlinger服务初始化完成后,在SurfaceFlinger的readyToRun()函数最后,将调用DisplayHardware对象Hw的startSleepManagement()函数来启动DisplayEventThread线程

 

 

?

1

2

3

4

5

6

7

void DisplayHardwareBase::startSleepManagement() const {

    if (mDisplayEventThread->initCheck() == NO_ERROR) {

        mDisplayEventThread->run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);

    } else {

        ALOGW("/sys/power/wait_for_fb_{wake|sleep} don't exist");

    }

}

当硬件帧缓冲区被打开时,帧缓冲区驱动程序就会创建/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake文件,用来通知用户空间显示屏即将要进入睡眠/唤醒状态。函数首先调用initCheck()函数来检查/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake文件是否存在,如果文件存在,则启动DisplayEventThread线程,DisplayEventThread线程的执行过程如下:

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

bool DisplayHardwareBase::DisplayEventThread::threadLoop() {

    //等待硬件帧缓冲区fb进入睡眠状态,如果fb进入睡眠,函数返回,否则函数阻塞

    if (waitForFbSleep() == NO_ERROR) {

        sp<surfaceflinger> flinger = mFlinger.promote();

        ALOGD("About to give-up screen, flinger = %p", flinger.get());

        //如果硬件帧缓冲区fb进入睡眠状态,则通知SurfaceFlinger释放显示屏

        if (flinger != 0) {

            flinger->screenReleased();

        }

        //等待硬件帧缓冲区fb进入唤醒状态,如果fb被唤醒,函数返回,否则函数阻塞

        if (waitForFbWake() == NO_ERROR) {

            ALOGD("Screen about to return, flinger = %p", flinger.get());

            //如果硬件帧缓冲区fb被唤醒,则通知SurfaceFlinger获取显示屏

            if (flinger != 0) {

                flinger->screenAcquired();

            }

            //线程循环执行threadLoop()函数

            return true;

        }

    }

    // error, exit the thread

    return false;

}</surfaceflinger>

在DisplayEventThread线程执行过程中,通过waitForFbSleep()和waitForFbWake()函数分别等待硬件帧缓冲区的睡眠/唤醒,当fb进入睡眠时,调用SurfaceFlinger的screenReleased()函数释放显示屏;当fb被唤醒时,调用SurfaceFlinger的screenAcquired()函数来获取显示屏。首先介绍fb睡眠监控过程:

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

status_t DisplayHardwareBase::DisplayEventThread::waitForFbSleep() {

    int err = 0;

    char buf;

    //kSleepFileName = "/sys/power/wait_for_fb_sleep";

    int fd = open(kSleepFileName, O_RDONLY, 0);

    // if the file doesn't exist, the error will be caught in read() below

    do {

        //读取"/sys/power/wait_for_fb_sleep"文件,如果文件不存在或者正确读取文件内容,该函数退出循环

        err = read(fd, &buf, 1);

    } while (err < 0 && errno == EINTR);

    close(fd);

    ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_SLEEP failed (%s)", strerror(errno));

    return err < 0 ? -errno : int(NO_ERROR);

}

我们知道,当硬件帧缓冲区fb进入睡眠状态时,fb驱动程序会通过写/sys/power/wait_for_fb_sleep文件来告知用户空间的应用程序当前硬件帧缓冲fb的状态,因此,DisplayEventThread线程只需读取/sys/power/wait_for_fb_sleep文件内容就可以判断fb是否进入睡眠状态。当fb进入睡眠状态时,DisplayEventThread线程就可以读取到/sys/power/wait_for_fb_sleep文件的内容,waitForFbSleep函数返回,否则循环读取/sys/power/wait_for_fb_sleep文件。同样,当硬件帧缓冲区fb被唤醒时,fb驱动程序会通过写/sys/power/wait_for_fb_wake文件来告知用户空间的应用程序当前硬件帧缓冲fb的状态:

 

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

status_t DisplayHardwareBase::DisplayEventThread::waitForFbWake() {

    int err = 0;

    char buf;

    //kWakeFileName  = "/sys/power/wait_for_fb_wake";

    int fd = open(kWakeFileName, O_RDONLY, 0);

    // if the file doesn't exist, the error will be caught in read() below

    do {

        //读取"/sys/power/wait_for_fb_wake"文件,如果文件不存在或者正确读取文件内容,该函数退出循环

        err = read(fd, &buf, 1);

    } while (err < 0 && errno == EINTR);

    close(fd);

    ALOGE_IF(err<0, "*** ANDROID_WAIT_FOR_FB_WAKE failed (%s)", strerror(errno));

    return err < 0 ? -errno : int(NO_ERROR);

}

当fb被唤醒时,DisplayEventThread线程就可以读取到/sys/power/wait_for_fb_wake文件的内容,waitForFbWake函数返回,否则循环读取/sys/power/wait_for_fb_wake文件。到此我们就知道DisplayEventThread线程是如何获取硬件帧缓冲区fb的状态,一旦DisplayEventThread线程监控到硬件帧缓冲区fb发生唤醒/睡眠状态切换,那么就会它通知SurfaceFlinger来处理。

 

1. 显示屏释放过程

 

当fb进入睡眠时,DisplayEventThread线程将调用SurfaceFlinger的screenReleased()函数来释放显示屏:

 

?

1

2

3

4

5

6

7

8

9

10

11

12

13

void SurfaceFlinger::screenReleased() {

    class MessageScreenReleased : public MessageBase {

        SurfaceFlinger* flinger;

    public:

        MessageScreenReleased(SurfaceFlinger* flinger) : flinger(flinger) { }

        virtual bool handler() {

            flinger->onScreenReleased();

            return true;

        }

    };

    sp<messagebase> msg = new MessageScreenReleased(this);

    postMessageSync(msg);

}</messagebase>

Android SurfaceFlinger服务的消息循环过程源码分析中介绍了SurfaceFlinger维护的消息循环过程。这里定义了一种释放显示屏的消息类型MessageScreenReleased,并通过postMessageSync()函数向SurfaceFlinger的消息队列同步发送一个MessageScreenReleased消息,由于发送的是一个同步消息,因此该函数将等待SurfaceFlinger的消息循环处理完该消息后才返回。当MessageScreenReleased消息被发送到SurfaceFlinger的消息队列后,SurfaceFlinger的消息循环将调用该消息的处理函数handler(),在MessageScreenReleased消息处理函数handler()中又继续调用SurfaceFlinger的onScreenReleased()函数来释放显示屏:

 

 

?

1

2

3

4

5

6

7

8

9

void SurfaceFlinger::onScreenReleased() {

    const DisplayHardware& hw(graphicPlane(0).displayHardware());

    if (hw.isScreenAcquired()) {

        //将显示屏释放转交给EventThread线程处理

        mEventThread->onScreenReleased();

        //设置DisplayHardwareBase类的成员变量mScreenAcquired为false

        hw.releaseScreen();

    }

}

 

函数首先调用EventThread线程的onScreenReleased()函数来唤醒EventThread线程,并且关闭VSync事件。

?

1

2

3

4

5

6

7

8

void EventThread::onScreenReleased() {

    Mutex::Autolock _l(mLock);

    if (!mUseSoftwareVSync) {

        // disable reliance on h/w vsync

        mUseSoftwareVSync = true;

        mCondition.broadcast();

    }

}

而当显示屏处于睡眠状态时,DisplayHardwareBase类的成员变量mScreenAcquired的值就会等于false,表示SurfaceFlinger服务不可以访问显示屏。

?

1

2

3

void DisplayHardwareBase::releaseScreen() const {

    mScreenAcquired = false;

}

 

2. 显示屏获取过程

当fb被唤醒时,DisplayEventThread线程将调用SurfaceFlinger的screenAcquired()函数来获取显示屏:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

void SurfaceFlinger::screenAcquired() {

    class MessageScreenAcquired : public MessageBase {

        SurfaceFlinger* flinger;

    public:

        MessageScreenAcquired(SurfaceFlinger* flinger) : flinger(flinger) { }

        virtual bool handler() {

            flinger->onScreenAcquired();

            return true;

        }

    };

    sp<messagebase> msg = new MessageScreenAcquired(this);

    postMessageSync(msg);

}</messagebase>

这里定义了一种获取显示屏的消息类型MessageScreenAcquired,同样往SurfaceFlinger的消息队列中同步发送一个MessageScreenAcquired消息,SurfaceFlinger的消息循环将调用该消息的handler()函数来处理该消息,在该消息的处理函数handler()中,调用SurfaceFlinger的onScreenAcquired()函数来获取显示屏:

?

1

2

3

4

5

6

7

8

9

10

void SurfaceFlinger::onScreenAcquired() {

    const DisplayHardware& hw(graphicPlane(0).displayHardware());

    hw.acquireScreen();

    mEventThread->onScreenAcquired();

    // this is a temporary work-around, eventually this should be called

    // by the power-manager

    SurfaceFlinger::turnElectronBeamOn(mElectronBeamAnimationMode);

    // from this point on, SF will process updates again

    repaintEverything();

}

首先调用DisplayHardware对象hw的acquireScreen()函数设置DisplayHardwareBase类的成员变量mScreenAcquired的值就会等于true,表示SurfaceFlinger服务可以访问显示屏。

?

1

2

3

void DisplayHardwareBase::acquireScreen() const {

    mScreenAcquired = true;

}

接着调用EventThread的onScreenAcquired()函数来唤醒EventThread线程,并打开VSync事件发送器。

?

1

2

3

4

5

6

7

8

void EventThread::onScreenAcquired() {

    Mutex::Autolock _l(mLock);

    if (mUseSoftwareVSync) {

        // resume use of h/w vsync

        mUseSoftwareVSync = false;

        mCondition.broadcast();

    }

}

然后调用SurfaceFlinger的turnElectronBeamOn()函数点亮显示屏

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

status_t SurfaceFlinger::turnElectronBeamOn(int32_t mode)

{

    class MessageTurnElectronBeamOn : public MessageBase {

        SurfaceFlinger* flinger;

        int32_t mode;

        status_t result;

    public:

        MessageTurnElectronBeamOn(SurfaceFlinger* flinger, int32_t mode)

            : flinger(flinger), mode(mode), result(PERMISSION_DENIED) {

        }

        status_t getResult() const {

            return result;

        }

        virtual bool handler() {

            Mutex::Autolock _l(flinger->mStateLock);

            result = flinger->turnElectronBeamOnImplLocked(mode);

            return true;

        }

    };

    postMessageAsync( new MessageTurnElectronBeamOn(this, mode) );

    return NO_ERROR;

}

和前面SurfaceFlinger处理显示屏的释放、获取类似,也是为显示屏点亮处理定义类型为MessageTurnElectronBeamOn的消息,并向SurfaceFlinger消息队列中发送一个该类型的消息,有一点不同的是,这里的消息采用异步方式发送。SurfaceFlinger消息循环将调用该消息的处理函数handler来点亮显示屏,在消息处理函数中又调用SurfaceFlinger的turnElectronBeamOnImplLocked函数来完成显示屏的点亮处理。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

status_t SurfaceFlinger::turnElectronBeamOnImplLocked(int32_t mode)

{

    DisplayHardware& hw(graphicPlane(0).editDisplayHardware());

    //通过DisplayHardwareBase类的成员变量mScreenAcquired来判断当前显示屏是否可用

    if (hw.canDraw()) {

        // we're already on

        return NO_ERROR;

    }

    //是否需要显示动画

    if (mode & ISurfaceComposer::eElectronBeamAnimationOn) {

        electronBeamOnAnimationImplLocked();

    }

    //设置脏区域为整个屏幕大小

    mDirtyRegion.set(hw.bounds());

    //请求VSync事件

    signalTransaction();

    return NO_ERROR;

}

该函数通过调用signalTransaction()函数来请求下一次VSync事件

?

1

2

3

void SurfaceFlinger::signalTransaction() {

    mEventQueue.invalidate();

}

变量mEventQueue的类型为MessageQueue,因此这里调用MessageQueue的invalidate()函数

?

1

2

3

void MessageQueue::invalidate() {

    mEvents->requestNextVsync();

}

在Android SurfaceFlinger对VSync信号的处理过程分析中介绍了SurfaceFlinger通过Connection来接收EventThread线程分发的VSync事件,这里调用Connection的requestNextVsync()函数请求EventThread线程分发下一次的VSync事件。

?

1

2

3

void EventThread::Connection::requestNextVsync() {

    mEventThread->requestNextVsync(this);

}

通过唤醒EventThread线程来请求下一次VSync事件

?

1

2

3

4

5

6

7

void EventThread::requestNextVsync(const sp<eventthread::connection>& connection) {

    Mutex::Autolock _l(mLock);

    if (connection->count < 0) {

        connection->count = 0;

        mCondition.broadcast();

    }

}</eventthread::connection>

回到onScreenAcquired()函数,最后调用repaintEverything()函数请求刷新显示屏。

?

1

2

3

4

5

6

void SurfaceFlinger::repaintEverything() {

    const DisplayHardware& hw(graphicPlane(0).displayHardware());

    const Rect bounds(hw.getBounds());

    setInvalidateRegion(Region(bounds));

    signalTransaction();

}

DisplayHardwareBase类用来控制SurfaceFlinger服务是否能够在显示屏上渲染UI。当DisplayHardwareBase类的成员函数canDraw的返回值等于true时,就表示SurfaceFlinger服务可以在显示屏上渲染系统的UI,否则就不可以。DisplayEventThreadBase类的创建一个名称为“DisplayEventThread”的线程,用来监控fb的睡眠/唤醒状态切换事件。这个线程循环调用DisplayEventThread类的成员函数threadLoop来监控fb的睡眠/唤醒状态切换事件,并根据fb的状态通知SurfaceFlinger释放或获取显示屏。

点赞