前言html
本次投稿活动正在进行中,奖品会由机械工业出版社华章公司
统一发出,感谢郭楚谋
同窗的投稿和支持,投稿活动还在继续,能够关注下android
福利活动算法
由Android研习社
和机械工业出版社华章公司
联合发起的赠书活动正在进行中,欢迎你们点击连接参与数组
在 Android 4.1 的时候,Google 提出了著名的 "Project Butter",引入了 VSYNC,把 app 画图,SurfaceFlinger 合成的时间点都规范了起来,减小了掉帧,加强了渲染的流畅度。可是这里有个问题,由于 VSYNC 是由硬件产生的,一旦产生了你就必须开始干活,不灵活。假设有这么一种需求,我但愿在 VSYNC 偏移一段时间之后再干活,那么这个是硬件 VSYNC 提供不了,因此这个时候就必须引入软件模型。而 DispSync 就是为了解决这个需求引入的软件模型。DispSync 相似于一个 PLL(phase lock loop,锁相回路),它经过接收硬件 VSYNC,而后给其余关心硬件 VSYNC 的组件(SurfaceFlinger 和须要渲染的 app)在指定的偏移之后发送软件 VSYNC,**而且当偏差在可接受的范围内,将会关闭硬件 VSYNC。**谷歌的[这篇文档][1]里面详细有一张很是准确的图:bash
![此处输入图片的描述][2]app
(为了方便,后面全部的硬件 VSYNC 使用 HW-VSYNC 代指,软件 VSYNC 使用 SW-VSYNC 代指)ide
前面提到 DispSync 是一个模拟 HW-VSYNC 的软件模型,在这个模型里面包含几个部分:函数
下面来详细描述一下整个初始化的流程。oop
首先说明一下,DispSync 的初始化流程初看是十分复杂的,首先它涉及到比较多的线程,而且线程在不少时候是处于阻塞状态的,致使整个流程处于一个不连续的状态。所以谁把哪一个线程唤醒了就变得十分重要,这也是理解整个初始化过程当中的一个难点。post
DispSync 在 SurfaceFlinger 里只有一个实例 —— mPrimaryDipsSync,它在 SurfaceFlinger 的初始化分两部分,建立实例 mPrimaryDispSync 而后执行其 init() 方法。DispSync 的构造函数很是简单,都是一些赋值:
DispSync::DispSync(const char* name)
: mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {}
explicit DispSyncThread(const char* name)
: mName(name),
mStop(false),
mPeriod(0),
mPhase(0),
mReferenceTime(0),
mWakeupLatency(0),
mFrameNumber(0) {}
复制代码
而后来看 init() 方法:
void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
mIgnorePresentFences = !hasSyncFramework;
mPresentTimeOffset = dispSyncPresentTimeOffset;
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
// set DispSync to SCHED_FIFO to minimize jitter
struct sched_param param = {0};
param.sched_priority = 2;
if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
}
reset();
beginResync();
...
}
复制代码
DispSycn::init()
最主要的就是工做就是让 DispSyncThread 运行起来,而且将其调度优先级改成 SCHED_FIFO
,这样作的目的是什么呢?咱们前面提到,DispSyncThread 大部分时间都在阻塞,它会“睡”到下次 SW-VSYNC 开始的时间戳,所以当其被唤醒的时候,高优先级可以保证其尽快地被调度,减小偏差。执行完 mThread->run()
之后,就会开始执行 DispSyncThread::threadLoop()
:
virtual bool threadLoop() {
status_t err;
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (true) {
Vector<CallbackInvocation> callbackInvocations;
nsecs_t targetTime = 0;
{ // Scope for lock
Mutex::Autolock lock(mMutex);
...
if (mStop) {
return false;
}
if (mPeriod == 0) {
err = mCond.wait(mMutex);
// 第一次初始化,因为 mPeriod 为 0,因此会先 block 在这里
复制代码
目前 DispSyncThread 会阻塞在这里,咱们接下去看。
在 SurfaceFlinger 初始化的时候,会建立两个 EventThread,一个给 SurfaceFlinger,一个给 app:
void SurfaceFlinger::init() {
...
// start the EventThread
mEventThreadSource =
std::make_unique<DispSyncSource>(&mPrimaryDispSync, SurfaceFlinger::vsyncPhaseOffsetNs,
true, "app");
mEventThread = std::make_unique<impl::EventThread>(mEventThreadSource.get(),
[this]() { resyncWithRateLimit(); },
impl::EventThread::InterceptVSyncsCallback(),
"appEventThread");
mSfEventThreadSource =
std::make_unique<DispSyncSource>(&mPrimaryDispSync,
SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread =
std::make_unique<impl::EventThread>(mSfEventThreadSource.get(),
[this]() { resyncWithRateLimit(); },
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
},
"sfEventThread");
复制代码
前面提到,DispSyncSource 是 DispSyncThread 和 EventThread 的中间人,先来看一下 DispSyncSource 的构造函数:
class DispSyncSource final : public VSyncSource, private DispSync::Callback {
public:
DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* name) :
mName(name),
mValue(0),
mTraceVsync(traceVsync),
mVsyncOnLabel(String8::format("VsyncOn-%s", name)),
mVsyncEventLabel(String8::format("VSYNC-%s", name)),
mDispSync(dispSync),
mCallbackMutex(),
mVsyncMutex(),
mPhaseOffset(phaseOffset),
mEnabled(false) {}
复制代码
请注意这里有一个很是重要的点,就是 mVsyncEventLabel(String8::format("VSYNC-%s", name))
。SurfaceFlinger 的 DispSyncSource 传进来的 name 是 "sf",app 的 DispSyncSource 传进来的 name 是 "app",因此连起来就是 "VSYNC-sf" 和 "VSYNC-app"。为何说重要呢?来看一段 systrace:
![Screenshot from 2019-06-26 21-20-38.png-71.5kB][3]
这里面的 VSYNC-app 和 VSYNC-sf 就是说的 DispSyncSource,至于它的意义,后面会提到。
而后 DispSyncSource 做为参数传给 EventThread 的构造函数:
EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
: mVSyncSource(src),
mResyncWithRateLimitCallback(resyncWithRateLimitCallback),
mInterceptVSyncsCallback(interceptVSyncsCallback) {
for (auto& event : mVSyncEvent) {
event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
event.header.id = 0;
event.header.timestamp = 0;
event.vsync.count = 0;
}
mThread = std::thread(&EventThread::threadMain, this);
pthread_setname_np(mThread.native_handle(), threadName);
pid_t tid = pthread_gettid_np(mThread.native_handle());
// Use SCHED_FIFO to minimize jitter
constexpr int EVENT_THREAD_PRIORITY = 2;
struct sched_param param = {0};
param.sched_priority = EVENT_THREAD_PRIORITY;
if (pthread_setschedparam(mThread.native_handle(), SCHED_FIFO, ¶m) != 0) {
ALOGE("Couldn't set SCHED_FIFO for EventThread");
}
set_sched_policy(tid, SP_FOREGROUND);
}
复制代码
构造函数的最主要功能就是把 EventThread 的线程主体 threadMain 运行起来而且设置其优先级为 SCHED_FIFO,接下来看 threadMain:
void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
std::unique_lock<std::mutex> lock(mMutex);
while (mKeepRunning) {
DisplayEventReceiver::Event event;
Vector<sp<EventThread::Connection> > signalConnections;
signalConnections = waitForEventLocked(&lock, &event);
// dispatch events to listeners...
const size_t count = signalConnections.size();
for (size_t i = 0; i < count; i++) {
const sp<Connection>& conn(signalConnections[i]);
// now see if we still need to report this event
status_t err = conn->postEvent(event);
...
}
}
复制代码
threadMain 的主要工做是调用 waitForEventLocked
等待一个 Event
,而后在一个个地通知 signalConnections
。至于这个 Event
和 signalConnections
分别是什么,后面会具体描述。如今先来看一下 waitForEventLocked
的逻辑:
// This will return when (1) a vsync event has been received, and (2) there was
// at least one connection interested in receiving it when we started waiting.
Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked(
std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* event) {
Vector<sp<EventThread::Connection> > signalConnections;
while (signalConnections.isEmpty() && mKeepRunning) {
bool eventPending = false;
bool waitForVSync = false;
size_t vsyncCount = 0;
nsecs_t timestamp = 0;
// 在前面 EventThread 的构造函数里面已经把 mVSyncEvent 数组内的全部 timestamp 都置为 0
// 所以在第一次初始化的时候,这个循环会直接退出
for (int32_t i = 0; i < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES; i++) {
...
}
// 第一次初始化的时候 mDisplayEventConnections 的数组也为空,count 为 0
size_t count = mDisplayEventConnections.size();
if (!timestamp && count) {
...
}
// 第一次初始化不执行这个循环
for (size_t i = 0; i < count;) {
...
}
// timestamp 为 0, waitForVSync 为 false
if (timestamp && !waitForVSync) {
...
} else if (!timestamp && waitForVSync) {
...
}
// eventPending 为 false,符合条件
if (!timestamp && !eventPending) {
if (waitForVSync) {
...
// waitForVSync 为 false,进入 else
} else {
// 最终,在第一次初始化的时候,EventThread 就阻塞在这里了
mCondition.wait(*lock);
}
}
}
...
}
复制代码
好,到这里,SurfaceFlinger 建立的两个 EventThread 都会阻塞在上面代码提到的地方,SurfaceFlinger 的初始化继续执行。
首先说明一下 mEventQueue 是在哪里被初始化的。是在 SurfaceFlinger 的另外一个方法:
提到这里就须要 SurfaceFlinger 是怎么启动和初始化的。SurfaceFlinger 做为系统最基本最核心的服务之一,是经过 init.rc 的方式进行启动的(内容在 frameworks/native/services/surfaceflinger/surfaceflinger.rc
):
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc input
onrestart restart zygote
...
复制代码
而后就须要提到 SurfaceFlinger 的组成部分,init.rc 里面提到的 /system/bin/surfaceflinger 这个二进制文件,由 main_surfaceflinger.cpp
这个文件编译获得;而上面提到 DispSync,EventThread 等,都被编译到了 libsurfaceflinger.so
这个库。这也给了咱们一个启示:当咱们在本身调试 SurfaceFlinger 的时候,大部分时间都只须要从新编译 libsurfaceflinger.so 这个文件便可。
回来简单看一下 SurfaceFlinger 是如何启动的,来看看 main_surfaceflinger.cpp
:
int main(int, char **) {
...
sp<SurfaceFlinger> flinger = DisplayUtils::getInstance()->getSFInstance();
...
flinger->init();
...
复制代码
这里的重点就是这个 sp<SurfaceFlinger>
,当被 sp 指针引用的时候,会触发 onFirstRef()
函数:
void SurfaceFlinger::onFirstRef()
{
mEventQueue->init(this);
}
复制代码
这样,就走到了 MessageQueue 部分了:
接着 EventThread,而后就执行到这里:
void SurfaceFlinger::init() {
...
mEventQueue->setEventThread(mSFEventThread.get());
复制代码
mEventQueue 在前面的 SurfaceFlinger::onFirstRef()
中完成了初始化:
void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
mFlinger = flinger;
mLooper = new Looper(true);
mHandler = new Handler(*this);
}
复制代码
接着来看一下很重要的 setEventThread()
:
void MessageQueue::setEventThread(android::EventThread* eventThread) {
if (mEventThread == eventThread) {
return;
}
if (mEventTube.getFd() >= 0) {
mLooper->removeFd(mEventTube.getFd());
}
mEventThread = eventThread;
mEvents = eventThread->createEventConnection();
mEvents->stealReceiveChannel(&mEventTube);
mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver,
this);
}
复制代码
重点来了,前面建立的 SurfaceFlinger 的 EventThread 被做为参数传给了 setEventThread
,而且执行了 EventThread 的 createEventConnection()
。(注意,须要时时刻刻地记住,如今处理的 SurfaceFlinger 的 EventThread)
(后面为了方便,将使用 sfEventThread 指代 SurfaceFlinger 的 EventThread;使用 appEventThread 指代 app 的 EventThread)
sp<BnDisplayEventConnection> EventThread::createEventConnection() const {
return new Connection(const_cast<EventThread*>(this));
}
复制代码
在这里,sfEventThread 迎来了第一个(同时也是惟一的) Connection:
EventThread::Connection::Connection(EventThread* eventThread)
: count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {}
void EventThread::Connection::onFirstRef() {
// NOTE: mEventThread doesn't hold a strong reference on us mEventThread->registerDisplayEventConnection(this); } status_t EventThread::registerDisplayEventConnection( const sp<EventThread::Connection>& connection) { std::lock_guard<std::mutex> lock(mMutex); mDisplayEventConnections.add(connection); mCondition.notify_all(); return NO_ERROR; } 复制代码
MessageQueue 调用 sfEventThread 的 createEventConnection
建立一个 Connection。因为 sp 指针的做用,将会调用 Connection::onFirstRef
,最终这个 Connection 会被添加到 mDisplayEventConnections
而且唤醒在 EventThread - 01 中阻塞的线程。
在前面把 EventThread 唤醒后,因为 signalConnections
为空,继续循环。而后因为新加入的 Connection count 为 -1,因此这个 EventThread 会继续阻塞,不过此时 mDisplayEventConnections
里面已经有一个 Connection 了。接着看下去。
SurfaceFlinger::init()
接着运行到这里:
void SurfaceFlinger::init() {
...
mEventControlThread = std::make_unique<impl::EventControlThread>(
[this](bool enabled) { setVsyncEnabled(HWC_DISPLAY_PRIMARY, enabled); });
复制代码
主要提一下的是,这个传进来的参数是一个 Lambda 表达式,具体的语法不讲。稍微解释一下这里传进来的 Lambda 表达式的意义就是,捕获列表为 SurfaceFlinger 自己,接受一个布尔参数,当这个 Lamda 表达式被调用的时候,会调用 SurfaceFlinger::setVsyncEnabled()
这个函数,这个函数后面会提到,也是一个很重要的函数。
EventControlThread 的构造函数的主要内容也是启动一个线程:
EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
: mSetVSyncEnabled(function) {
pthread_setname_np(mThread.native_handle(), "EventControlThread");
pid_t tid = pthread_gettid_np(mThread.native_handle());
setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
set_sched_policy(tid, SP_FOREGROUND);
}
void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
auto keepRunning = true;
auto currentVsyncEnabled = false;
while (keepRunning) {
mSetVSyncEnabled(currentVsyncEnabled);
std::unique_lock<std::mutex> lock(mMutex);
// keepRunning 为 true,currentVsyncEnabled 为 false,mVsyncEnabled 默认值为 false,mKeepRunning 默认值为 true,所以 Lambda 表达式为 false,线程阻塞
mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
});
currentVsyncEnabled = mVsyncEnabled;
keepRunning = mKeepRunning;
}
}
复制代码
此时,EventControlThread 也会陷入阻塞之中。而 SurfaceFlinger 也将迎来初始化中最为复杂的一步。
至此,SurfaceFlinger 总共起了四个线程 —— DispSyncThread,两个 EvenThread 和 EventControlThread,而且这四个线程全都处于阻塞状态。致使这些线程处于阻塞状态的缘由是:
mPeriod
为 0Connection->count
为 -1mVsyncEnabled
为 false而后让咱们一个个将其唤醒。
接下来的 SurfaceFlinger 会进行很是复杂的初始化操做,EventThread 唤醒相关的调用流程以下(这里借用了这位大佬[《Android SurfaceFlinger SW Vsync模型》][4]的内容,写得很是棒,在学习的过程当中可以获得了很大的启发):
initializeDisplays();
flinger->onInitializeDisplays();
setTransactionState(state, displays, 0);
setTransactionFlags(transactionFlags);
signalTransaction();
mEventQueue->invalidate();
mEvents->requestNextVsync() //mEvents是Connection实例
EventThread->requestNextVsync(this);
复制代码
void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
...
if (connection->count < 0) {
connection->count = 0;
mCondition.notify_all();
}
}
复制代码
在这里把前面建立的那个 Connection 的 count 置为 0,而且唤醒阻塞的 EventThread,这个时候,mDisplayEventConnections 不为空而且 count 不为 -1,能够正常地运行了,EventThread::waitForEventLocked()
走到了这里:
} else if (!timestamp && waitForVSync) {
// we have at least one client, so we want vsync enabled
// (TODO: this function is called right after we finish
// notifying clients of a vsync, so this call will be made
// at the vsync rate, e.g. 60fps. If we can accurately
// track the current state we could avoid making this call
// so often.)
enableVSyncLocked();
}
void EventThread::enableVSyncLocked() {
// 通常都为 false
if (!mUseSoftwareVSync) {
// never enable h/w VSYNC when screen is off
if (!mVsyncEnabled) {
mVsyncEnabled = true;
mVSyncSource->setCallback(this);
mVSyncSource->setVSyncEnabled(true);
}
}
mDebugVsyncEnabled = true;
}
复制代码
调用了 DispSyncSource::setCallback()
,将 EventThread 和 DispSyncSource 联系在了一块儿:
void setCallback(VSyncSource::Callback* callback) override{
Mutex::Autolock lock(mCallbackMutex);
mCallback = callback;
}
复制代码
接着调用 DispSyncSource::setVSyncEnabled
:
void setVSyncEnabled(bool enable) override {
Mutex::Autolock lock(mVsyncMutex);
// true
if (enable) {
status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
static_cast<DispSync::Callback*>(this));
...
}
复制代码
最终调用了 DispSync::addEventListener
:
status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) {
if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
// 保证了 mEventListeners 的惟一性
for (size_t i = 0; i < mEventListeners.size(); i++) {
if (mEventListeners[i].mCallback == callback) {
return BAD_VALUE;
}
}
EventListener listener;
listener.mName = name;
listener.mPhase = phase;
listener.mCallback = callback;
listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;
mEventListeners.push(listener);
// 唤醒 DispSyncThread
mCond.signal();
return NO_ERROR;
}
复制代码
把 DispSyncSource 加到 mEventListeners,将 DispSync 和 DispSyncSource 联系在了一块儿,而且把前面阻塞的 DispSyncThread 唤醒,可是因为 mPeriod 仍是为 0,所以 DispSyncThread 仍是会继续阻塞。
不过此时从调用关系已经初步能够看到前面我说的那句 DispSyncSource 是 DispSync 和 EventThread 的中间人 是正确的了。
接着来看 DispSyncThread。
设置 mPeriod
的流程以下(依旧引用了这位大佬的[《Android SurfaceFlinger SW Vsync模型》][4]的内容,再次感谢):
initializeDisplays();
flinger->onInitializeDisplays();
setPowerModeInternal()
resyncToHardwareVsync(true);
repaintEverything();
复制代码
这里把 SurfaceFlinger::resyncToHardwareVsync()
分为两部分,先看上部分:
void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) {
Mutex::Autolock _l(mHWVsyncLock);
if (makeAvailable) {
mHWVsyncAvailable = true;
} else if (!mHWVsyncAvailable) {
// Hardware vsync is not currently available, so abort the resync
// attempt for now
return;
}
const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY);
const nsecs_t period = activeConfig->getVsyncPeriod();
mPrimaryDispSync.reset();
// 设置 mPeriod
mPrimaryDispSync.setPeriod(period);
// 默认为 false
if (!mPrimaryHWVsyncEnabled) {
mPrimaryDispSync.beginResync();
// 上部分结束
复制代码
在 DispSync::setPeriod()
里面给 mPeriod
赋值,而且把 DispSyncThread
唤醒:
void DispSync::setPeriod(nsecs_t period) {
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = 0;
mReferenceTime = 0;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
if (kTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = phase;
mReferenceTime = referenceTime;
ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64
" mReferenceTime = %" PRId64,
mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime));
// 这里把 DispSyncThread 唤醒
mCond.signal();
}
复制代码
至此,DispSyncThread 也开始运转。
接着看 SurfaceFlinger::resyncToHardwareVsync()
的下半部分:
...
mEventControlThread->setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
}
void EventControlThread::setVsyncEnabled(bool enabled) {
std::lock_guard<std::mutex> lock(mMutex);
mVsyncEnabled = enabled;
// 把 EventControlThread 唤醒
mCondition.notify_all();
}
复制代码
把 EventControlThread 唤醒之后,会从新把 SurfaceFlinger 传进来的那个被 Lambda 表达式包裹的 SurfaceFlinger::setVsyncEnabled()
从新执行一下:
void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) {
ATRACE_CALL();
Mutex::Autolock lock(mStateLock);
getHwComposer().setVsyncEnabled(disp,
enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
}
void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) {
if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) {
ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId);
return;
}
RETURN_IF_INVALID_DISPLAY(displayId);
// NOTE: we use our own internal lock here because we have to call
// into the HWC with the lock held, and we want to make sure
// that even if HWC blocks (which it shouldn't), it won't
// affect other threads.
Mutex::Autolock _l(mVsyncLock);
auto& displayData = mDisplayData[displayId];
if (enabled != displayData.vsyncEnabled) {
ATRACE_CALL();
auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
RETURN_IF_HWC_ERROR(error, displayId);
displayData.vsyncEnabled = enabled;
char tag[16];
snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId);
// 在 systrace 看到的就是在这里
ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0);
}
}
复制代码
在这里,真正地去开启 HW-VSync。而后因为 SurfaceFlinger 接收了 HW-VSync,而后展转发给 DispSync,DispSync 接收,校订 SW-VSYNC。而整个 DispSync SurfaceFlinger 部分的初始化的流程也最终完成。
注意,上面说的是 SurfaceFlinger 部分。前面提到,总共有两个 EventThread,而上面分析的都是 sfEventThread,下面简单地描述一下 appEventThread 的流程,其实 EventThread 到 DispSync 这部分都是一致的,只是 EventThread 的 Connection 的注册流程不同。sfEventThread 是 MessageQueue 去注册 Connection,而 appEventThread 则是另外一种方法。
SurfaceFlinger 接收 VSYNC 是为了合成,所以 sfEventThread 的 Connection 只有一个,就是 SurfaceFlinger 自己;而 app 接收 VSYNC 是为了画帧,appEventThread 会有不少不少个 Connection。
app 自己是如何在 appEventThread 注册一个 Connection 的,与这篇文章的主体有点偏移,这个能够另开一篇文章来详细说明,流程也是很是复杂,这里只简单地描述:核心就是 libgui 下面的 DisplayEventReceiver
,它在初始化的时候会调用 SurfaceFlinger::createEventConnection
:
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
ISurfaceComposer::VsyncSource vsyncSource) {
if (vsyncSource == eVsyncSourceSurfaceFlinger) {
return mSFEventThread->createEventConnection();
} else {
return mEventThread->createEventConnection();
}
}
复制代码
而后后面的流程就跟前面的一致了。
经过上面的描述,依据各个类的依赖关系,其实能够总结出这么一个图:
![][7]
请注意箭头方向。
前面提到,引入 DispSync 的目的是为了经过 SF-VSYNC 来模拟 HW-VSYNC 的行为而且经过加入 offset 来让通知时机变得灵活。所以理解整个 DispSync 的流程就能够归结为下面几个部分:SF-VSYNC 通知周期 mPeriod 的计算;SF-VSYNC 的模拟方式以及 SF-VSYNC 传递流程,分别来看。
前面提到,DispSync 经过接收 HW-VSYNC 而且更新计算出 SW-VSYNC 间隔—— mPeriod,首先看一下 DispSync 是如何收到 HW-VSYNC。
先看一下 SurfaceFlinger 这个类:
class SurfaceFlinger : public BnSurfaceComposer,
public PriorityDumper,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback
复制代码
SurfaceFlinger 实现了 HW2::ComposerCallback
的接口,而后当 HW-VSYNC 到来的时候,HWC 会将 HW-VSYNC 发生的时间戳发给 SurfaceFlinger,而后 SurfaceFlinger 会转发给 DispSync:
class ComposerCallbackBridge : public Hwc2::IComposerCallback {
public:
...
Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
{
mCallback->onVsyncReceived(mSequenceId, display, timestamp);
return Void();
}
...
};
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId,
hwc2_display_t displayId, int64_t timestamp) {
...
{ // Scope for the lock
Mutex::Autolock _l(mHWVsyncLock);
if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) {
needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
}
}
// 这个很重要,后面会提到
if (needsHwVsync) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
复制代码
重点看 DispSync 怎么处理这些 HW-VSYNC,是在 addResyncSample()
这个函数:
bool DispSync::addResyncSample(nsecs_t timestamp) {
Mutex::Autolock lock(mMutex);
size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
mResyncSamples[idx] = timestamp;
if (mNumResyncSamples == 0) {
mPhase = 0;
mReferenceTime = timestamp;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
mNumResyncSamples++;
} else {
mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
}
updateModelLocked();
if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
resetErrorLocked();
}
...
bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
return !modelLocked;
}
复制代码
这里须要重点说明这里面几个变量的意义(在 DispSync.h 这个头文件里面有说明):
DispSync 将从 SurfaceFlinger 发来的 HW-VSYNC 的时间戳都给记录到一个 ring buffer,当有了足够多的 HW-VSYNC 了之后(目前是 6 个即以上),就能够开始来拟合 SF-VSYNC 的间隔 mPeriod 了,是在 DispSync::updateModelLocked()
里面计算的,核心算法就在这里了。分为两部分,一部分是 mPeriod 的计算:
void DispSync::updateModelLocked() {
if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
nsecs_t durationSum = 0;
nsecs_t minDuration = INT64_MAX;
nsecs_t maxDuration = 0;
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
durationSum += duration;
minDuration = min(minDuration, duration);
maxDuration = max(maxDuration, duration);
}
durationSum -= minDuration + maxDuration;
mPeriod = durationSum / (mNumResyncSamples - 3);
...
复制代码
mPeriod 的计算十分简单,把全部的 HW-VSYNC 先后相减算出 HW-VSYNC 的时间间隔,而后去掉一个最小值和最大值,而后全部 HW-VSYNC 的时间戳之和除以总个数就是 mPeriod 了。这里有一个问题就是为何在最后除的时候是除数是 3?其实很简单,由于前面的 for 循环是从 1 开始算起的,因此循环结束一下 durationSum 实际上是 mNumResyncSamples - 1 个 HW-VSYNC 的总和,而后再去掉一个最大和最小,因此总数是 mNumResyncSamples - 3。
另外一部分是 mPhase 的计算,这一块看上去好像挺复杂的,甚至还有三角函数:
...
double sampleAvgX = 0;
double sampleAvgY = 0;
// scale 的意义是,每 ms 表明了多少度。(总量除以总个数等于每一个的值)
double scale = 2.0 * M_PI / double(mPeriod);
// Intentionally skip the fist sample
for (size_t i = 1; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
// sample 是偏差
nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
// 这里 (sample % mPeriod) 看上去挺唬人的,可是其实就是保证 sample 不会大于或者等于 mPeriod,不然这里的 samplePhase 算出来就是 2π 了
// 因此这里 samplePhase 算出来的就是把偏差转成度数
double samplePhase = double(sample % mPeriod) * scale;
// 这两个后面是为了用来计算偏差平均的度数
sampleAvgX += cos(samplePhase);
sampleAvgY += sin(samplePhase);
}
sampleAvgX /= double(mNumResyncSamples - 1);
sampleAvgY /= double(mNumResyncSamples - 1);
// 根据等比关系,算出平局偏差度数对应的 ns 值
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase));
if (mPhase < -(mPeriod / 2)) {
mPhase += mPeriod;
ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
}
if (kTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
}
// Artificially inflate the period if requested.
mPeriod += mPeriod * mRefreshSkipCount;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
mModelUpdated = true;
复制代码
上面的逻辑其实能够用下图来阐述:
而 mPhase 最终是根据下面的等比公式计算出来的:
最后,看一下 DispSync::addResyncSample
这个函数的返回值,这个返回值很是重要,当经过统计 SW-VSYNC 的偏差小于阈值的时候(这个偏差的计算涉及到了 Fence,目前我对这部份内容理解得还不是很透彻,等完全理解了之后再来填坑),返回 true 给 SurfaceFlinger 的时候,SurfaceFlinger 则会调用 SurfaceFlinger::disableHardwareVsync
把 HW-VSYNC 给关了。
mPeriod 计算出来之后,DispSyncThread 就能够依据这个值来模拟 HW-VSYNC 了(实际上计算流程和模拟流程是相互独立的,分别在两个不一样的线程上完成),因此流程都在 DispSyncThread 的 threadLoop() 里面:
virtual bool threadLoop() {
status_t err;
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
while (true) {
Vector<CallbackInvocation> callbackInvocations;
nsecs_t targetTime = 0;
{ // Scope for lock
Mutex::Autolock lock(mMutex);
if (mStop) {
return false;
}
if (mPeriod == 0) {
err = mCond.wait(mMutex);
if (err != NO_ERROR) {
ALOGE("error waiting for new events: %s (%d)", strerror(-err), err);
return false;
}
continue;
}
// 这里计算出下一个确切的 SW-VSYNC 的时间戳
targetTime = computeNextEventTimeLocked(now);
bool isWakeup = false;
if (now < targetTime) {
if (targetTime == INT64_MAX) {
err = mCond.wait(mMutex);
} else {
// 睡到下一个 SW-VSYNC 为止
err = mCond.waitRelative(mMutex, targetTime - now);
}
if (err == TIMED_OUT) {
isWakeup = true;
} else if (err != NO_ERROR) {
return false;
}
}
now = systemTime(SYSTEM_TIME_MONOTONIC);
// Don't correct by more than 1.5 ms static const nsecs_t kMaxWakeupLatency = us2ns(1500); if (isWakeup) { mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency); } callbackInvocations = gatherCallbackInvocationsLocked(now); } if (callbackInvocations.size() > 0) { fireCallbackInvocations(callbackInvocations); } } return false; } 复制代码
这里经过依据 mPeriod 算出下一个 SW-VSYNC 的时间戳,计算 SW-VSYNC 的时间戳的逻辑比较简单,就不过多描述。而后经过条件变量直接睡到下一个 SW-VSYNC,而后一个个地经过调用 DispSyncSource 的 onDispSyncEvent 回调来进行 SW-VSYNC 的通知。而后 DispSyncSource 的 onDispSyncEvent 又会调用 EventThread 的 onVSyncEvent:
void EventThread::onVSyncEvent(nsecs_t timestamp) {
std::lock_guard<std::mutex> lock(mMutex);
mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
mVSyncEvent[0].header.id = 0;
mVSyncEvent[0].header.timestamp = timestamp;
mVSyncEvent[0].vsync.count++;
mCondition.notify_all();
}
复制代码
这里就能够回答一下在提到的问题,mVSyncEvent 和 mDisplayEventConnections 以及 signalConnections 这三个数组的意义和区别:
enum DisplayType {
DISPLAY_ID_INVALID = -1,
DISPLAY_PRIMARY = HWC_DISPLAY_PRIMARY,
DISPLAY_EXTERNAL = HWC_DISPLAY_EXTERNAL,
DISPLAY_VIRTUAL = HWC_DISPLAY_VIRTUAL,
NUM_BUILTIN_DISPLAY_TYPES = HWC_NUM_PHYSICAL_DISPLAY_TYPES,
};
复制代码
从这里就能够看到,至少在这个版本的 Android 除了一个 virtual display(这是 SurfaceFlinger 提供的一个很是有用的功能,不少常见的需求例如录屏就是经过 virtual display 来实现的,这里不展开,有须要的话再写一篇文章详细描述)已是支持多屏幕了,只不过呢,目前的代码里面都是写死只处理主屏,也就是 Display 0 的事件。
EventThread::Connection
的数组,在调用 EventThread::registerDisplayEventConnection()
的时候,就会把这个 Connection 加到这个数组里面。EventThread::waitForEventLocked
最大的做用就是返回这个数组,这个数组存的是全部但愿接收下一个 SW-VSYNC 的 Connection,而是否接收 Connection 的标志是 connection->count 的值:-1 表明不接收 SW-VSYNC;0 表明只接收一次,EventThread 发现 connection->count 的值为 0 的时候,会把它加到 signalConnections 以便其可以接受到这一次的 SW-VSYNC 以后,会将其 count 置为 -1;大于 0 就代表会一直接收。onVSyncEvent 的做用是新增一个 VSyncEvent 而且把 EventThread 唤醒,EventThread 统计了全部对 SW-VSYNC 感兴趣的 Connection 而且都加到 signalConnections,最后会经过一个循环调用每一个 connection 的 postEvent() 函数,SurfaceFlinger 就会开始走合成的流程,app 就会开始走渲染的流程。至此,SW-VSYNC 完成了传递的全过程。
当整个初始化完成之后,整个 DispSync 模型就开始运做起来了。咱们先简单地把整个流程描述一下:
SurfaceFlinger 经过实现了
HWC2::ComposerCallback
接口,当 HW-VSYNC 到来的时候,SurfaceFlinger 将会收到回调而且发给 DispSync。DispSync 将会把这些 HW-VSYNC 的时间戳记录下来,当累计了足够的 HW-VSYNC 之后(目前是大于等于 6 个),就开始计算 SW-VSYNC 的偏移 mPeriod。计算出来的 mPeriod 将会用于 DispSyncThread 用来模拟 HW-VSYNC 的周期性起来而且通知对 VSYNC 感兴趣的 Listener,这些 Listener 包括 SurfaceFlinger 和全部须要渲染画面的 app。这些 Listener 经过 EventThread 以 Connection 的抽象形式注册到 EventThread。DispSyncThread 与 EventThread 经过 DispSyncSource 做为中间人进行链接。EventThread 在收到 SW-VSYNC 之后将会把通知全部感兴趣的 Connection,而后 SurfaceFlinger 开始合成,app 开始画帧。在收到足够多的 HW-VSYNC 而且在偏差容许的范围内,将会关闭经过 EventControlThread 关闭 HW-VSYNC。
而后这个流程咱们能够获得下面这张跟初始化很是接近,只是方向相反的 SW-VSYNC 的传递图:
![][8]
写了这么多内容,可能不少人仍是没法理解引入软件模型的意义所在,前面咱们提到是让整个流程更加灵活这句话可能也不是很好理解,所以在这里详细描述一下。
首先呢,先来看一下 DispSync 的第一个提交的 commit message,它详细地描述了引入了 DispSync 的初衷:
commit faf77cce9d9ec0238d6999b3bd0d40c71ff403c5
Author: Jamie Gennis <jgennis@google.com>
Date: Tue Jul 30 15:10:32 2013 -0700
SurfaceFlinger: SW-based vsync events
This change adds the DispSync class, which models the hardware vsync event
times to allow vsync event callbacks to be done at an arbitrary phase offset
from the hardware vsync. This can be used to reduce the minimum latency from
Choreographer wake-up to on-screen image presentation.
Bug: 10624956
Change-Id: I8c7a54ceacaa4d709726ed97b0dcae4093a7bdcf
复制代码
意思就是但愿可以经过 DispSync 来减小 app 渲染的内容到屏幕的事件延迟,也就是传说中的跟手性。这里须要说明一下从 app 渲染画面到显示到屏幕的一个简易 pipeline(这部份内容参考了[这篇博客][5],建议细读,写得十分好!)。
首先须要说明的是,为了严格保证显示的流畅,防止画面撕裂的状况发生,画面更新到屏幕面板须要在 HW-VSYNC 开始的时候才作。
没有 DispSync 的时候:
从上面这个简易的 pipeline 能够看到,App 画的帧得得两个 HW-VSYNC 以后才能显示到屏幕面板上,也就是大概 33.3ms。可是,如今大部分的状况是,硬件的性能已经足够快了,画一帧的时间和合成的时间不须要一个 HW-VSYNC 了,这个时候 DispSync 的做用就来了。经过引入 offset,当 offset 为正值时,App 和 SurfaceFlinger 都是在 HW-VSYNC 日后 offset ms 才开始工做的,这个时候 App 画帧到最终显示到面板上的延迟就变成了 (2 * VSYNC_PERIOD - (offset % VSYNC_PERIOD)),这样就变相地减小了这个延迟,加强了跟手性,其实这个就是当初引入 DispSync 的初衷。
反过来能够这么想,假设把 offset 变为负值,这个时候 App 渲染和 SurfaceFlinger 合成可用的时间就变长了,在某些负载比较重的场景,这个能够优化渲染性能。
甚至还有这种状况,假设在某些场景,App 渲染和 SurfaceFlinger 合成的总时间都足够短,那么若是设置合理的话,例如 app 的 offset 设置为 0,SurfaceFlinger 的 offset 设置为 VSYNC_PERIOD/2,那么就可以保证 App 渲染到显示到面板的时间差在一个 HW-VSYNC 内完成。
从上面的分析就能够看到,这个就是引入软件模型的灵活性的体现,根据不一样的需求对 offset 进行不一样的取值,能够获得不一样的效果。
学了 DispSync 有什么用呢?其实不是说学了 DispSync 有用,而是透过 DispSync 咱们学到了 VSYNC 分发的整个流程,这个可以去解释不少问题。这里举一个例子。前段时间一加 7 Pro 推出了首款 90 Hz 屏幕的手机,不少评测机构都纷纷表示,微博滑动等界面感受更加流畅了,这背后的原理是什么呢?这个时候就可使用前面学到的知识来分析一波了。这里的 90 Hz 指的就是 HW-VSYNC。而后根据前面的渲染 pipeline,在没有 DispSync 的状况下,因为 HW-VSYNC 的从普通的 60 Hz变成了 90 Hz,VSYNC 的时间间隔从 16.6ms 减小到了 11.1ms,从前面的 pipeline 能够得出,app 从渲染到显示的延迟减小了 10ms 左右,这个延迟减小是十分明显的,所以会有一个“流畅”的感受。所以可否这么想的,当屏幕的刷新率变成了 90 Hz 甚至是 120 Hz 之后,DispSync 的做用可能就愈来愈小了,那个时候谷歌会不会把它去掉呢?这个能够看一下后面 Android 的改动,至少在目前,在这个 90 Hz 即将普及的今天,Android Q 的 DispSync 仍是保留着的。
[1]: https://source.android.com/devices/graphics/implement-vsync
[2]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc9e14b7d50?w=500&h=134&f=png&s=7554
[3]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc8b37fee0a?w=1920&h=332&f=png&s=73263
[4]: https://www.jianshu.com/p/d3e4b1805c92
[5]: http://echuang54.blogspot.com/2015/01/dispsync.html
[6]: https://source.android.com/devices/graphics/implement-vsync.html#explicit_synchronization
[7]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc8b17ecf1b?w=952&h=590&f=png&s=50643
[8]: https://user-gold-cdn.xitu.io/2019/11/3/16e2fbc8b19009fe?w=1244&h=590&f=png&s=58913
复制代码
郑重声明
本文由Android研习社代发,原做者为郭楚谋
,版权归原做者全部,未经容许,禁止转载,侵权必究!