AndroidQ 应用层Vsync信号的注册与接收(下)

上一篇文章主要分析了java层DisplayEventReceiver中的nativeInit函数,这个函数主要作用就是创建了应用层和surfaceFlinger的连接,通过一对socket,对应mReceiveFd和mSendFd,应用层通过native层Looper将mReceiveFd加入监听,随时等待mSendFd的写入

这篇文章就来分析什么时候mSendFd会写入数据,以及如何传递到应用层,核心函数nativeScheduleVsync,这个函数用于应用层向native层注册监听下一次Vsync信号,在UI需要刷新时调用

nativeScheduleVsync

//android_view_DisplayEventReceiver.cpp
static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
 
    sp<NativeDisplayEventReceiver> receiver =
            reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
    status_t status = receiver->scheduleVsync();
    if (status) {
        String8 message;
        message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
    }
}

nativeInit时java层保存了NativeDisplayEventReceiver的指针,这里将它传递到native层,强转为NativeDisplayEventReceiver对象,并调用它的scheduleVsync函数

scheduleVsync

NativeDisplayEventReceiver继承DisplayEventReceiver,scheduleVsync这个函数是在父类实现的

status_t DisplayEventDispatcher::scheduleVsync() {
    if (!mWaitingForVsync) {
        ALOGV("dispatcher %p ~ Scheduling vsync.", this);

        // Drain all pending events.
        nsecs_t vsyncTimestamp;
        PhysicalDisplayId vsyncDisplayId;
        uint32_t vsyncCount;
        //步骤1
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
                    this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
        }
        //步骤2
        status_t status = mReceiver.requestNextVsync();
        if (status) {
            ALOGW("Failed to request next vsync, status=%d", status);
            return status;
        }

        mWaitingForVsync = true;
    }
    return OK;
}

mWaitingForVsync用来避免短时间重复请求,等一个Vsync处理完之后才能处理下一个,这个函数分为两个步骤来分析

我们先看步骤1,processPendingEvents这个函数的作用看名字大概是处理正在准备中的事件

processPendingEvents

bool DisplayEventDispatcher::processPendingEvents(
        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                // Later vsync events will just overwrite the info from earlier
                // ones. That's fine, we only care about the most recent.
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                ......
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
               .....
                break;
            default:
                
                break;
            }
        }
    }
    ...
    return gotVsync;
}

这个函数主要作用就是通过mReceiver.getEvents获取事件,mReceiver类型为DisplayEventReceiver,然后处理事件,获取到的事件放入大小为100的buf中,
static const size_t EVENT_BUFFER_SIZE = 100

然后根据不同类型的事件,调用不同的处理方式,事件有三种,定义在
DisplayEventReceiver.h中

    //DisplayEventReceiver.h
    enum {
        DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'),
        DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'),
        DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'),
    };
    

我们这里只关注VSYNC事件,来看看如何获取

mReceiver.getEvents

ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
        size_t count) {
    return DisplayEventReceiver::getEvents(mDataChannel.get(), events, count);
}

ssize_t DisplayEventReceiver::getEvents(gui::BitTube* dataChannel,
        Event* events, size_t count)
{
    return gui::BitTube::recvObjects(dataChannel, events, count);
}

前一篇文章分析过,mDataChannel是在nativeInit过程中创建的,这个过程创建了一套”app“的连接用来接收Vsync信号,而gui::BitTube则作为这套连接的数据收发的管理类

gui::BitTube::recvObjects

//BitTube.h
template <typename T>
    static ssize_t recvObjects(BitTube* tube, T* events, size_t count) {
        return recvObjects(tube, events, count, sizeof(T));
    }

三个参数recvObjects调用内部四个参数的recvObjects

ssize_t BitTube::recvObjects(BitTube* tube, void* events, size_t count, size_t objSize) {

    char* vaddr = reinterpret_cast<char*>(events);

    ssize_t size = tube->read(vaddr, count * objSize);
    .....
    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}

先来说一下recvObjects的参数,tube不用说,events指向一个DisplayEventReceiver::Event的buff数组,count为buff的长度100,objSize为每个DisplayEventReceiver::Event的大小

recvObjects函数中最重要的就是tube->read这个函数,来看看

tube->read

ssize_t BitTube::read(void* vaddr, size_t size) {
    ssize_t err, len;
    do {
        len = ::recv(mReceiveFd, vaddr, size, MSG_DONTWAIT);
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    if (err == EAGAIN || err == EWOULDBLOCK) {
        return 0;
    }
    return err == 0 ? len : -err;
}

我们看到read这个函数就是调用recv函数来接收数据,到这里我们来简单说说socket中send和recv函数:

socket在内核中有一个发送缓冲区和一个接收缓冲区,send函数负责将数据存入socket的发送缓冲区,recv函数负责从socket的接收缓冲区中将数据拷贝到用户空间,recv和send不一定是一一对应,也就是说并不是send一次,就一定recv一次接收完,有可能send一次,recv多次才能接收完,也可能send多次,一次recv就接收完了

简单理解就是send发数据,recv就能够收到数据,再来看看recv函数的参数是什么意思:

int recv(mReceiveFd, vaddr, size, MSG_DONTWAIT)

mReceiveFd是接收数据的socket描述符,mReceiveFd这个Fd在上一篇文章分析过,它会接收来自mSendFd的数据

vaddr指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据

size指明缓冲区的大小

MSG_DONTWAIT是一个flag,为0

recv函数的意思就是将mReceiveFd接收到的数据,放入大小为size的vaddr缓冲区,最后返回其实际接收的字节数,如果recv在接收数据时出错,那么它返回SOCKET_ERROR(-1),如果recv在等待接收数据时网络中断了,那么它返回0。

到这里我们回到DisplayEventDispatcher的processPendingEvents函数:

bool DisplayEventDispatcher::processPendingEvents(
        nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, uint32_t* outCount) {
    bool gotVsync = false;
    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
    ssize_t n;
    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
        for (ssize_t i = 0; i < n; i++) {
            const DisplayEventReceiver::Event& ev = buf[i];
            switch (ev.header.type) {
            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                ....
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
                ...
                break;
            case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
                ...
                break;
            default:
                ...
                break;
            }
        }
    }
    if (n < 0) {
        ...
    }
    return gotVsync;
}

这个函数其实逻辑挺简单的,调用mReceiver.getEvents读取mReceiveFd的数据,放入buf中,buf的长度为100,然后遍历此数组,拿出所有的Event,对应不同类型,做不同处理

我们再来看看DisplayEventReceiver::Event这个结构体

struct Event {

        struct Header {
            uint32_t type;
            PhysicalDisplayId displayId;
            nsecs_t timestamp __attribute__((aligned(8)));
        };

        struct VSync {
            uint32_t count;
        };

        struct Hotplug {
            bool connected;
        };

        struct Config {
            int32_t configId;
        };

        Header header;
        union {
            VSync vsync;
            Hotplug hotplug;
            Config config;
        };
    };

这个Event结构体包含三种类型的Event,用一个union来描述,每种类型的Event都有它们特有的变量,VSync有count,Hotplug有connected,Config有configId,另外还有一个公有的结构体Header,Header中包含一个Event的类型,一个DisplayId,一个Event的时间戳

我们只关注Event的类型是Vsync的情况,走如下处理分支:

case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
                ....
                gotVsync = true;
                *outTimestamp = ev.header.timestamp;
                *outDisplayId = ev.header.displayId;
                *outCount = ev.vsync.count;
                break;

这个分支其实含义就是将Vsync的timestamp,displayId,count全部保存下来,因为是循环遍历,所以如果buf中的Vsync非常多,那么只会保存最新的的那个Vsync的信息

到此processPendingEvents这个函数已经分析完了,它的作用是获取最新到来的Vsync信号,并保存下它的timestamp,displayId,count,并返回gotVsync这个bool值代表是否获取到了Vsync

再把scheduleVsync函数贴出来看看

status_t DisplayEventDispatcher::scheduleVsync() {
    if (!mWaitingForVsync) {
        nsecs_t vsyncTimestamp;
        PhysicalDisplayId vsyncDisplayId;
        uint32_t vsyncCount;
        //步骤1
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "",
                    this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
        }
        //步骤2
        status_t status = mReceiver.requestNextVsync();
        if (status) {
            ALOGW("Failed to request next vsync, status=%d", status);
            return status;
        }
        mWaitingForVsync = true;
    }
    return OK;
}

我们会发现processPendingEvents在scheduleVsync中调用似乎没什么实际作用,不管返回true还是false都会继续请求Vsync

我们继续看步骤2,这一看就是请求Vsync的操作

mReceiver.requestNextVsync

status_t DisplayEventReceiver::requestNextVsync() {
    if (mEventConnection != nullptr) {
        mEventConnection->requestNextVsync();
        return NO_ERROR;
    }
    return NO_INIT;
}

在前一篇文章分析过,mEventConnection是在nativeInit过程中创建的,类型为BpDisplayEventConnection,
这里最终通过Binder调到surfaceFlinger进程中BnDisplayEventConnection的实现类EventThreadConnection中,

//EventThread.cpp
void EventThreadConnection::requestNextVsync() {
    ATRACE_NAME("requestNextVsync");
    mEventThread->requestNextVsync(this);
}

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
    if (connection->resyncCallback) {
        connection->resyncCallback();
    }

    std::lock_guard<std::mutex> lock(mMutex);

    if (connection->vsyncRequest == VSyncRequest::None) {
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();
    }
}

resyncCallback在前一篇文章分析过,resyncCallback类型为ResyncCallback,定义在EventThread.cpp中,std::function这是个新API,大致功能是将一段可执行函数封装到ResyncCallback中,仅从功能上看,类似函数指针

using ResyncCallback = std::function<void()>;

在nativeInit过程中,创建EventThreadConnection时创建的resyncCallback,并将它保存在了EventThreadConnection的resyncCallback成员变量中,resyncCallback中封装了如下这段代码

ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
    std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
    return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
        if (const auto vsync = ptr.lock()) {
            vsync->resync(getVsyncPeriod);
        }
    };
}

所以connection->resyncCallback不为空,接着调用resyncCallback()函数,调用resyncCallback时就相当于执行makeResyncCallback return的这段代码,即vsync->resync

void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
    //将500ms换算成ns
    static constexpr nsecs_t kIgnoreDelay = ms2ns(500);
    //获取当前时间
    const nsecs_t now = systemTime();
    const nsecs_t last = lastResyncTime.exchange(now);
    LOGI("kIgnoreDelay = :%ld,now = :%ld,last = :%ld",long(kIgnoreDelay),long(now),long(last));

    if (now - last > kIgnoreDelay) {
        scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
    }
}

在分析到这里的时候我在上面代码中打了log,发现(now - last > kIgnoreDelay)这个条件几乎返回的都是false,所以应用层向注册Vsync肯定不是通过resync后续进行的,我现在还不清楚这个函数的意义,这个函数暂时不分析了

继续回到requestNextVsync函数,我们跳过resync

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

    if (connection->resyncCallback) {
        //不是最终注册Vsync的代码,不清楚它的意义,暂时跳过
        connection->resyncCallback();
    }

    std::lock_guard<std::mutex> lock(mMutex);
   
    if (connection->vsyncRequest == VSyncRequest::None) {
        connection->vsyncRequest = VSyncRequest::Single;
        mCondition.notify_all();
    }
}

VSyncRequest定义在EventThread.h中

enum class VSyncRequest {
    None = -1,
    Single = 0,
    Periodic = 1,
    // Subsequent values are periods.
};

代表了当前VSync请求的三种状态,None为初始值,Single代表已经请求了一个VSync,是一次性的,Periodic代表请求的是周期性信号

我们收到VSync是需要自己请求的,请求一次,接收一次,而底层的VSync是以周期性在发送的,通常情况为16.6ms一次,所以这里定义了这三种VSync请求的状态

看到这里就能够知道为什么应用层请求的Vsync是一次性的了
当VSyncRequest为none时,给它赋值为Single,接着调用 mCondition.notify_all()函数

mCondition定义在EventThread.h中,类型为std::condition_variable,这是一个条件变量,它在这里最大的用处就是唤醒/等待
这里引用转 C++11 并发指南std::condition_variable详解对条件变量的介绍

std::condition_variable 是条件变量
当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notify 函数来唤醒当前线程。
std::condition_variable 对象通常使用 std::unique_lock<std::mutex> 来等待,如果需要使用另外的 lockable 类型,可以使用 std::condition_variable_any 类

我们主要关注这里条件变量使用notify_all会唤醒所有调用了wait的线程,wait在哪里调用的呢?是在EventThread创建的时候,它的构造函数中创建了一个线程,这个线程一启动就调用了threadMain函数,threadMain中通过条件变量调用wait使线程陷入等待

EventThread是在SurfaceFlinger的init中就创建了,有两种类型,一个名为”app“,一个名为”sf“,需要注意把EventThread和EventThreadConnection分开

EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
      : mVSyncSource(src),
        mVSyncSourceUnique(std::move(uniqueSrc)),
        mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
        mThreadName(threadName) {
    ...
    mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
        std::unique_lock<std::mutex> lock(mMutex);
        threadMain(lock);
    });
    ...
}

分析threadMain函数之前先看看EventThread.h中定义的几种状态

enum class State {
        Idle,   //空闲
        Quit,   //退出
        SyntheticVSync, 
        VSync,
    };
    //默认值为Idle
    State mState GUARDED_BY(mMutex) = State::Idle;

SyntheticVSync和VSync状态是互斥的:
mVSyncState->synthetic ? State::SyntheticVSync : State::VSync

mVSyncState->synthetic会在手机亮屏是被赋值为false,灭屏时赋值为true
threadMain比较复杂,主要作用是处理VSync的分发,它其实是一个死循环,只要线程没有退出

void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
    DisplayEventConsumers consumers;

    while (mState != State::Quit) {
        ...
        if (!mPendingEvents.empty()) {
            event = mPendingEvents.front();
            ....

        if (!consumers.empty()) {
            dispatchEvent(*event, consumers);
            consumers.clear();
        }

        ...

        // Wait for event or client registration/request.
        if (mState == State::Idle) {
            mCondition.wait(lock);
        } else {
             ...
        }
    }
}

我这里不详细分析这个函数,后面再写一篇文章分析,threadMain这个函数大致功能就是我留下的部分代码:
如果mPendingEvents不为空,就获取其头部事件,然后寻找能够消耗此事件的EventThreadConnection,找到了就调用dispatchEvent分发事件,如果mState为Idle,就让此线程陷入等待,唤醒条件就是调用notify函数

dispatchEvent分发VSync的流程其实也很简单,最终就是调用gui::BitTube::sendObjects函数

ssize_t BitTube::sendObjects(BitTube* tube, void const* events, size_t count, size_t objSize) {
    const char* vaddr = reinterpret_cast<const char*>(events);
    ssize_t size = tube->write(vaddr, count * objSize);
    ......
    return size < 0 ? size : size / static_cast<ssize_t>(objSize);
}
ssize_t BitTube::write(void const* vaddr, size_t size) {
    ssize_t err, len;
    do {
        len = ::send(mSendFd, vaddr, size, MSG_DONTWAIT | MSG_NOSIGNAL);
        // cannot return less than size, since we're using SOCK_SEQPACKET
        err = len < 0 ? errno : 0;
    } while (err == EINTR);
    return err == 0 ? len : -err;
}

sendObjects和recvObjects对应的,sendObjects函数再调用内部write函数,write函数就是向mSendFd中写数据,mSendFd和mReceiveFd对应一对可连接的socket,并且前一篇文章我们知道了nativeInit过程中mReceiveFd会被添加到handler的epoll中进行监听,可参考AndroidQ Handler消息机制(native层),所以当mSendFd写入数据之后,对应的mReceiveFd就能收到,并且会调用handleEvent回调函数,这个回调是在添加mReceiveFd时一并注册的:

status_t DisplayEventDispatcher::initialize() {
    ...
    int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
            this, NULL);
    if (rc < 0) {
        return UNKNOWN_ERROR;
    }
    return OK;
}

此回调的实现就在DisplayEventDispatcher中:

int DisplayEventDispatcher::handleEvent(int, int events, void*) {
    ...
    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 ", displayId=%"
                ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
                this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
        mWaitingForVsync = false;
        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
    }

    return 1; // keep the callback
}

processPendingEvents这个函数前面已经分析过了,主要作用就是通过mReceiver.getEvents获取事件,mReceiver类型为DisplayEventReceiver,然后处理事件,获取到的事件放入大小为100的buf中,mReceiver.getEvents最终是通过gui::BitTube::recvObjects函数获取mReceiveFd接收到的Vsync事件,并且只会处理最新的一次Vsync事件

事件收到之后通过dispatchVsync进行分发,dispatchVsync的实现在DisplayEventDispatcher子类NativeDisplayEventReceiver中:

void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId,
                                               uint32_t count) {
    ......
        env->CallVoidMethod(receiverObj.get(),
                gDisplayEventReceiverClassInfo.dispatchVsync, timestamp, displayId, count);
       ......
}

这里通过JNI调用java层DisplayEventReceiver的dispatchVsync方法就将Vsync信号相关信息发送到了应用层:

 private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
    }

然后回调了onVsync方法,到这里整个Vsync信号向native层注册与应用层接收的整个流程就已经连通了,核心就是一对互连的socket,然后通过C++ 11提供的std::condition_variable条件变量的唤醒/等待机制来完成Vsync的注册与监听

应用层向native层请求Vsync信号的实质就是通过std::condition_variable唤醒当前处于wait状态的EventThread,被唤醒之后就会处理mPendingEvents中的Vsync信号,然后分发,分发的实质就是通过gui::BitTube::sendObjects向mSendFd写入数据,然后通过handler监听的mReceiveFd就能收到数据并调用回调函数handleEvent,此回调中就将Vsync信息分发到了应用层,并调用onVsync方法开始处理View的绘制工作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章