上一篇文章主要分析了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的绘制工作