SF: Move relevant scheduler files into one directory.
Scheduler (see go/surface-flinger-scheduler) is going to live in its own
directory. This CL just moves the relevant files into that directory. No
changes to business logic.
Test: all SF tests pass.
Change-Id: Iff0717f9867316b28e68fd8311bd9fdc4e029951
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
new file mode 100644
index 0000000..9d9acd3
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -0,0 +1,729 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <math.h>
+
+#include <algorithm>
+
+#include <log/log.h>
+#include <utils/String8.h>
+#include <utils/Thread.h>
+#include <utils/Trace.h>
+
+#include <ui/FenceTime.h>
+
+#include "DispSync.h"
+#include "EventLog/EventLog.h"
+#include "SurfaceFlinger.h"
+
+using std::max;
+using std::min;
+
+namespace android {
+
+DispSync::~DispSync() = default;
+
+namespace impl {
+
+// Setting this to true enables verbose tracing that can be used to debug
+// vsync event model or phase issues.
+static const bool kTraceDetailedInfo = false;
+
+// Setting this to true adds a zero-phase tracer for correlating with hardware
+// vsync events
+static const bool kEnableZeroPhaseTracer = false;
+
+// This is the threshold used to determine when hardware vsync events are
+// needed to re-synchronize the software vsync model with the hardware. The
+// error metric used is the mean of the squared difference between each
+// present time and the nearest software-predicted vsync.
+static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared
+
+#undef LOG_TAG
+#define LOG_TAG "DispSyncThread"
+class DispSyncThread : public Thread {
+public:
+ explicit DispSyncThread(const char* name)
+ : mName(name),
+ mStop(false),
+ mPeriod(0),
+ mPhase(0),
+ mReferenceTime(0),
+ mWakeupLatency(0),
+ mFrameNumber(0) {}
+
+ virtual ~DispSyncThread() {}
+
+ 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));
+ mCond.signal();
+ }
+
+ void stop() {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+ mStop = true;
+ mCond.signal();
+ }
+
+ virtual bool threadLoop() {
+ status_t err;
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ while (true) {
+ std::vector<CallbackInvocation> callbackInvocations;
+
+ nsecs_t targetTime = 0;
+
+ { // Scope for lock
+ Mutex::Autolock lock(mMutex);
+
+ if (kTraceDetailedInfo) {
+ ATRACE_INT64("DispSync:Frame", mFrameNumber);
+ }
+ ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber);
+ ++mFrameNumber;
+
+ 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;
+ }
+
+ targetTime = computeNextEventTimeLocked(now);
+
+ bool isWakeup = false;
+
+ if (now < targetTime) {
+ if (kTraceDetailedInfo) ATRACE_NAME("DispSync waiting");
+
+ if (targetTime == INT64_MAX) {
+ ALOGV("[%s] Waiting forever", mName);
+ err = mCond.wait(mMutex);
+ } else {
+ ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime));
+ err = mCond.waitRelative(mMutex, targetTime - now);
+ }
+
+ if (err == TIMED_OUT) {
+ isWakeup = true;
+ } else if (err != NO_ERROR) {
+ ALOGE("error waiting for next event: %s (%d)", strerror(-err), err);
+ 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);
+ if (kTraceDetailedInfo) {
+ ATRACE_INT64("DispSync:WakeupLat", now - targetTime);
+ ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
+ }
+ }
+
+ callbackInvocations = gatherCallbackInvocationsLocked(now);
+ }
+
+ if (callbackInvocations.size() > 0) {
+ fireCallbackInvocations(callbackInvocations);
+ }
+ }
+
+ return false;
+ }
+
+ status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ 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;
+
+ // We want to allow the firstmost future event to fire without
+ // allowing any past events to fire
+ listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency;
+
+ mEventListeners.push_back(listener);
+
+ mCond.signal();
+
+ return NO_ERROR;
+ }
+
+ status_t removeEventListener(DispSync::Callback* callback) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ for (std::vector<EventListener>::iterator it = mEventListeners.begin();
+ it != mEventListeners.end(); ++it) {
+ if (it->mCallback == callback) {
+ mEventListeners.erase(it);
+ mCond.signal();
+ return NO_ERROR;
+ }
+ }
+
+ return BAD_VALUE;
+ }
+
+ status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ Mutex::Autolock lock(mMutex);
+
+ for (auto& eventListener : mEventListeners) {
+ if (eventListener.mCallback == callback) {
+ const nsecs_t oldPhase = eventListener.mPhase;
+ eventListener.mPhase = phase;
+
+ // Pretend that the last time this event was handled at the same frame but with the
+ // new offset to allow for a seamless offset change without double-firing or
+ // skipping.
+ nsecs_t diff = oldPhase - phase;
+ if (diff > mPeriod / 2) {
+ diff -= mPeriod;
+ } else if (diff < -mPeriod / 2) {
+ diff += mPeriod;
+ }
+ eventListener.mLastEventTime -= diff;
+ mCond.signal();
+ return NO_ERROR;
+ }
+ }
+
+ return BAD_VALUE;
+ }
+
+private:
+ struct EventListener {
+ const char* mName;
+ nsecs_t mPhase;
+ nsecs_t mLastEventTime;
+ DispSync::Callback* mCallback;
+ };
+
+ struct CallbackInvocation {
+ DispSync::Callback* mCallback;
+ nsecs_t mEventTime;
+ };
+
+ nsecs_t computeNextEventTimeLocked(nsecs_t now) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ ALOGV("[%s] computeNextEventTimeLocked", mName);
+ nsecs_t nextEventTime = INT64_MAX;
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now);
+
+ if (t < nextEventTime) {
+ nextEventTime = t;
+ }
+ }
+
+ ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime));
+ return nextEventTime;
+ }
+
+ std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now));
+
+ std::vector<CallbackInvocation> callbackInvocations;
+ nsecs_t onePeriodAgo = now - mPeriod;
+
+ for (auto& eventListener : mEventListeners) {
+ nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo);
+
+ if (t < now) {
+ CallbackInvocation ci;
+ ci.mCallback = eventListener.mCallback;
+ ci.mEventTime = t;
+ ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName);
+ callbackInvocations.push_back(ci);
+ eventListener.mLastEventTime = t;
+ }
+ }
+
+ return callbackInvocations;
+ }
+
+ nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName,
+ ns2us(baseTime));
+
+ nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
+ ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime));
+ if (baseTime < lastEventTime) {
+ baseTime = lastEventTime;
+ ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName, ns2us(baseTime));
+ }
+
+ baseTime -= mReferenceTime;
+ ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime));
+ nsecs_t phase = mPhase + listener.mPhase;
+ ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase));
+ baseTime -= phase;
+ ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime));
+
+ // If our previous time is before the reference (because the reference
+ // has since been updated), the division by mPeriod will truncate
+ // towards zero instead of computing the floor. Since in all cases
+ // before the reference we want the next time to be effectively now, we
+ // set baseTime to -mPeriod so that numPeriods will be -1.
+ // When we add 1 and the phase, we will be at the correct event time for
+ // this period.
+ if (baseTime < 0) {
+ ALOGV("[%s] Correcting negative baseTime", mName);
+ baseTime = -mPeriod;
+ }
+
+ nsecs_t numPeriods = baseTime / mPeriod;
+ ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods);
+ nsecs_t t = (numPeriods + 1) * mPeriod + phase;
+ ALOGV("[%s] t = %" PRId64, mName, ns2us(t));
+ t += mReferenceTime;
+ ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t));
+
+ // Check that it's been slightly more than half a period since the last
+ // event so that we don't accidentally fall into double-rate vsyncs
+ if (t - listener.mLastEventTime < (3 * mPeriod / 5)) {
+ t += mPeriod;
+ ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t));
+ }
+
+ t -= mWakeupLatency;
+ ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t));
+
+ return t;
+ }
+
+ void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
+ if (kTraceDetailedInfo) ATRACE_CALL();
+ for (size_t i = 0; i < callbacks.size(); i++) {
+ callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
+ }
+ }
+
+ const char* const mName;
+
+ bool mStop;
+
+ nsecs_t mPeriod;
+ nsecs_t mPhase;
+ nsecs_t mReferenceTime;
+ nsecs_t mWakeupLatency;
+
+ int64_t mFrameNumber;
+
+ std::vector<EventListener> mEventListeners;
+
+ Mutex mMutex;
+ Condition mCond;
+};
+
+#undef LOG_TAG
+#define LOG_TAG "DispSync"
+
+class ZeroPhaseTracer : public DispSync::Callback {
+public:
+ ZeroPhaseTracer() : mParity(false) {}
+
+ virtual void onDispSyncEvent(nsecs_t /*when*/) {
+ mParity = !mParity;
+ ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
+ }
+
+private:
+ bool mParity;
+};
+
+DispSync::DispSync(const char* name)
+ : mName(name), mRefreshSkipCount(0), mThread(new DispSyncThread(name)) {}
+
+DispSync::~DispSync() {}
+
+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();
+
+ if (kTraceDetailedInfo && kEnableZeroPhaseTracer) {
+ mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
+ addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
+ }
+}
+
+void DispSync::reset() {
+ Mutex::Autolock lock(mMutex);
+ resetLocked();
+}
+
+void DispSync::resetLocked() {
+ mPhase = 0;
+ mReferenceTime = 0;
+ mModelUpdated = false;
+ mNumResyncSamples = 0;
+ mFirstResyncSample = 0;
+ mNumResyncSamplesSincePresent = 0;
+ resetErrorLocked();
+}
+
+bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
+ Mutex::Autolock lock(mMutex);
+
+ if (mIgnorePresentFences) {
+ return true;
+ }
+
+ mPresentFences[mPresentSampleOffset] = fenceTime;
+ mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
+ mNumResyncSamplesSincePresent = 0;
+
+ updateErrorLocked();
+
+ return !mModelUpdated || mError > kErrorThreshold;
+}
+
+void DispSync::beginResync() {
+ Mutex::Autolock lock(mMutex);
+ ALOGV("[%s] beginResync", mName);
+ mModelUpdated = false;
+ mNumResyncSamples = 0;
+}
+
+bool DispSync::addResyncSample(nsecs_t timestamp) {
+ Mutex::Autolock lock(mMutex);
+
+ ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
+
+ size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
+ mResyncSamples[idx] = timestamp;
+ if (mNumResyncSamples == 0) {
+ mPhase = 0;
+ mReferenceTime = timestamp;
+ ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, "
+ "mReferenceTime = %" PRId64,
+ mName, ns2us(mPeriod), ns2us(mReferenceTime));
+ 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();
+ }
+
+ if (mIgnorePresentFences) {
+ // If we're ignoring the present fences we have no way to know whether
+ // or not we're synchronized with the HW vsyncs, so we just request
+ // that the HW vsync events be turned on.
+ return true;
+ }
+
+ // Check against kErrorThreshold / 2 to add some hysteresis before having to
+ // resync again
+ bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2);
+ ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked");
+ return !modelLocked;
+}
+
+void DispSync::endResync() {}
+
+status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback) {
+ Mutex::Autolock lock(mMutex);
+ return mThread->addEventListener(name, phase, callback);
+}
+
+void DispSync::setRefreshSkipCount(int count) {
+ Mutex::Autolock lock(mMutex);
+ ALOGD("setRefreshSkipCount(%d)", count);
+ mRefreshSkipCount = count;
+ updateModelLocked();
+}
+
+status_t DispSync::removeEventListener(Callback* callback) {
+ Mutex::Autolock lock(mMutex);
+ return mThread->removeEventListener(callback);
+}
+
+status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) {
+ Mutex::Autolock lock(mMutex);
+ return mThread->changePhaseOffset(callback, phase);
+}
+
+void DispSync::setPeriod(nsecs_t period) {
+ Mutex::Autolock lock(mMutex);
+ mPeriodBase = mPeriod = period;
+ mPhase = 0;
+ mReferenceTime = 0;
+ mThread->updateModel(mPeriod, mPhase, mReferenceTime);
+}
+
+void DispSync::scalePeriod(uint32_t multiplier, uint32_t divisor) {
+ Mutex::Autolock lock(mMutex);
+
+ // if only 1 of the properties is updated, we will get to this
+ // point "attempting" to set the scale to 1 when it is already
+ // 1. Check that special case so that we don't do a useless
+ // update of the model.
+ if ((multiplier == 1) && (divisor == 1) && (mPeriod == mPeriodBase)) return;
+
+ mPeriod = mPeriodBase * multiplier / divisor;
+ mThread->updateModel(mPeriod, mPhase, mReferenceTime);
+}
+
+nsecs_t DispSync::getPeriod() {
+ // lock mutex as mPeriod changes multiple times in updateModelLocked
+ Mutex::Autolock lock(mMutex);
+ return mPeriod;
+}
+
+void DispSync::updateModelLocked() {
+ ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples);
+ if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
+ ALOGV("[%s] Computing...", mName);
+ 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);
+ }
+
+ // Exclude the min and max from the average
+ durationSum -= minDuration + maxDuration;
+ mPeriodBase = mPeriod = durationSum / (mNumResyncSamples - 3);
+
+ ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
+
+ double sampleAvgX = 0;
+ double sampleAvgY = 0;
+ double scale = 2.0 * M_PI / double(mPeriod);
+ // Intentionally skip the first sample
+ for (size_t i = 1; i < mNumResyncSamples; i++) {
+ size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+ nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
+ double samplePhase = double(sample % mPeriod) * scale;
+ sampleAvgX += cos(samplePhase);
+ sampleAvgY += sin(samplePhase);
+ }
+
+ sampleAvgX /= double(mNumResyncSamples - 1);
+ sampleAvgY /= double(mNumResyncSamples - 1);
+
+ 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;
+ }
+}
+
+void DispSync::updateErrorLocked() {
+ if (!mModelUpdated) {
+ return;
+ }
+
+ // Need to compare present fences against the un-adjusted refresh period,
+ // since they might arrive between two events.
+ nsecs_t period = mPeriod / (1 + mRefreshSkipCount);
+
+ int numErrSamples = 0;
+ nsecs_t sqErrSum = 0;
+
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ // Only check for the cached value of signal time to avoid unecessary
+ // syscalls. It is the responsibility of the DispSync owner to
+ // call getSignalTime() periodically so the cache is updated when the
+ // fence signals.
+ nsecs_t time = mPresentFences[i]->getCachedSignalTime();
+ if (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) {
+ continue;
+ }
+
+ nsecs_t sample = time - mReferenceTime;
+ if (sample <= mPhase) {
+ continue;
+ }
+
+ nsecs_t sampleErr = (sample - mPhase) % period;
+ if (sampleErr > period / 2) {
+ sampleErr -= period;
+ }
+ sqErrSum += sampleErr * sampleErr;
+ numErrSamples++;
+ }
+
+ if (numErrSamples > 0) {
+ mError = sqErrSum / numErrSamples;
+ mZeroErrSamplesCount = 0;
+ } else {
+ mError = 0;
+ // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam.
+ mZeroErrSamplesCount++;
+ ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0,
+ "No present times for model error.");
+ }
+
+ if (kTraceDetailedInfo) {
+ ATRACE_INT64("DispSync:Error", mError);
+ }
+}
+
+void DispSync::resetErrorLocked() {
+ mPresentSampleOffset = 0;
+ mError = 0;
+ mZeroErrSamplesCount = 0;
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ mPresentFences[i] = FenceTime::NO_FENCE;
+ }
+}
+
+nsecs_t DispSync::computeNextRefresh(int periodOffset) const {
+ Mutex::Autolock lock(mMutex);
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t phase = mReferenceTime + mPhase;
+ return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
+}
+
+void DispSync::setIgnorePresentFences(bool ignore) {
+ Mutex::Autolock lock(mMutex);
+ if (mIgnorePresentFences != ignore) {
+ mIgnorePresentFences = ignore;
+ resetLocked();
+ }
+}
+
+void DispSync::dump(String8& result) const {
+ Mutex::Autolock lock(mMutex);
+ result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
+ result.appendFormat("mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod,
+ 1000000000.0 / mPeriod, mRefreshSkipCount);
+ result.appendFormat("mPhase: %" PRId64 " ns\n", mPhase);
+ result.appendFormat("mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError));
+ result.appendFormat("mNumResyncSamplesSincePresent: %d (limit %d)\n",
+ mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT);
+ result.appendFormat("mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples, MAX_RESYNC_SAMPLES);
+
+ result.appendFormat("mResyncSamples:\n");
+ nsecs_t previous = -1;
+ for (size_t i = 0; i < mNumResyncSamples; i++) {
+ size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+ nsecs_t sampleTime = mResyncSamples[idx];
+ if (i == 0) {
+ result.appendFormat(" %" PRId64 "\n", sampleTime);
+ } else {
+ result.appendFormat(" %" PRId64 " (+%" PRId64 ")\n", sampleTime,
+ sampleTime - previous);
+ }
+ previous = sampleTime;
+ }
+
+ result.appendFormat("mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES);
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ previous = Fence::SIGNAL_TIME_INVALID;
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES;
+ nsecs_t presentTime = mPresentFences[idx]->getSignalTime();
+ if (presentTime == Fence::SIGNAL_TIME_PENDING) {
+ result.appendFormat(" [unsignaled fence]\n");
+ } else if (presentTime == Fence::SIGNAL_TIME_INVALID) {
+ result.appendFormat(" [invalid fence]\n");
+ } else if (previous == Fence::SIGNAL_TIME_PENDING ||
+ previous == Fence::SIGNAL_TIME_INVALID) {
+ result.appendFormat(" %" PRId64 " (%.3f ms ago)\n", presentTime,
+ (now - presentTime) / 1000000.0);
+ } else {
+ result.appendFormat(" %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n", presentTime,
+ presentTime - previous, (presentTime - previous) / (double)mPeriod,
+ (now - presentTime) / 1000000.0);
+ }
+ previous = presentTime;
+ }
+
+ result.appendFormat("current monotonic time: %" PRId64 "\n", now);
+}
+
+} // namespace impl
+
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
new file mode 100644
index 0000000..183966f
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_DISPSYNC_H
+#define ANDROID_DISPSYNC_H
+
+#include <stddef.h>
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <ui/FenceTime.h>
+
+#include <memory>
+
+namespace android {
+
+class String8;
+class FenceTime;
+
+class DispSync {
+public:
+ class Callback {
+ public:
+ virtual ~Callback() = default;
+ virtual void onDispSyncEvent(nsecs_t when) = 0;
+ };
+
+ virtual ~DispSync();
+
+ virtual void reset() = 0;
+ virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
+ virtual void beginResync() = 0;
+ virtual bool addResyncSample(nsecs_t timestamp) = 0;
+ virtual void endResync() = 0;
+ virtual void setPeriod(nsecs_t period) = 0;
+ virtual void scalePeriod(const uint32_t multiplier, uint32_t divisor) = 0;
+ virtual nsecs_t getPeriod() = 0;
+ virtual void setRefreshSkipCount(int count) = 0;
+ virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0;
+ virtual status_t removeEventListener(Callback* callback) = 0;
+ virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
+ virtual nsecs_t computeNextRefresh(int periodOffset) const = 0;
+ virtual void setIgnorePresentFences(bool ignore) = 0;
+
+ virtual void dump(String8& result) const = 0;
+};
+
+namespace impl {
+
+class DispSyncThread;
+
+// DispSync maintains a model of the periodic hardware-based vsync events of a
+// display and uses that model to execute period callbacks at specific phase
+// offsets from the hardware vsync events. The model is constructed by
+// feeding consecutive hardware event timestamps to the DispSync object via
+// the addResyncSample method.
+//
+// The model is validated using timestamps from Fence objects that are passed
+// to the DispSync object via the addPresentFence method. These fence
+// timestamps should correspond to a hardware vsync event, but they need not
+// be consecutive hardware vsync times. If this method determines that the
+// current model accurately represents the hardware event times it will return
+// false to indicate that a resynchronization (via addResyncSample) is not
+// needed.
+class DispSync : public android::DispSync {
+public:
+ explicit DispSync(const char* name);
+ ~DispSync() override;
+
+ void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset);
+
+ // reset clears the resync samples and error value.
+ void reset() override;
+
+ // addPresentFence adds a fence for use in validating the current vsync
+ // event model. The fence need not be signaled at the time
+ // addPresentFence is called. When the fence does signal, its timestamp
+ // should correspond to a hardware vsync event. Unlike the
+ // addResyncSample method, the timestamps of consecutive fences need not
+ // correspond to consecutive hardware vsync events.
+ //
+ // This method should be called with the retire fence from each HWComposer
+ // set call that affects the display.
+ bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override;
+
+ // The beginResync, addResyncSample, and endResync methods are used to re-
+ // synchronize the DispSync's model to the hardware vsync events. The re-
+ // synchronization process involves first calling beginResync, then
+ // calling addResyncSample with a sequence of consecutive hardware vsync
+ // event timestamps, and finally calling endResync when addResyncSample
+ // indicates that no more samples are needed by returning false.
+ //
+ // This resynchronization process should be performed whenever the display
+ // is turned on (i.e. once immediately after it's turned on) and whenever
+ // addPresentFence returns true indicating that the model has drifted away
+ // from the hardware vsync events.
+ void beginResync() override;
+ bool addResyncSample(nsecs_t timestamp) override;
+ void endResync() override;
+
+ // The setPeriod method sets the vsync event model's period to a specific
+ // value. This should be used to prime the model when a display is first
+ // turned on. It should NOT be used after that.
+ void setPeriod(nsecs_t period) override;
+
+ // The scalePeriod method applies the multiplier and divisor to
+ // scale the vsync event model's period. The function is added
+ // for an experimental test mode and should not be used outside
+ // of that purpose.
+ void scalePeriod(const uint32_t multiplier, uint32_t divisor);
+
+ // The getPeriod method returns the current vsync period.
+ nsecs_t getPeriod() override;
+
+ // setRefreshSkipCount specifies an additional number of refresh
+ // cycles to skip. For example, on a 60Hz display, a skip count of 1
+ // will result in events happening at 30Hz. Default is zero. The idea
+ // is to sacrifice smoothness for battery life.
+ void setRefreshSkipCount(int count) override;
+
+ // addEventListener registers a callback to be called repeatedly at the
+ // given phase offset from the hardware vsync events. The callback is
+ // called from a separate thread and it should return reasonably quickly
+ // (i.e. within a few hundred microseconds).
+ status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override;
+
+ // removeEventListener removes an already-registered event callback. Once
+ // this method returns that callback will no longer be called by the
+ // DispSync object.
+ status_t removeEventListener(Callback* callback) override;
+
+ // changePhaseOffset changes the phase offset of an already-registered event callback. The
+ // method will make sure that there is no skipping or double-firing on the listener per frame,
+ // even when changing the offsets multiple times.
+ status_t changePhaseOffset(Callback* callback, nsecs_t phase) override;
+
+ // computeNextRefresh computes when the next refresh is expected to begin.
+ // The periodOffset value can be used to move forward or backward; an
+ // offset of zero is the next refresh, -1 is the previous refresh, 1 is
+ // the refresh after next. etc.
+ nsecs_t computeNextRefresh(int periodOffset) const override;
+
+ // In certain situations the present fences aren't a good indicator of vsync
+ // time, e.g. when vr flinger is active, or simply aren't available,
+ // e.g. when the sync framework isn't present. Use this method to toggle
+ // whether or not DispSync ignores present fences. If present fences are
+ // ignored, DispSync will always ask for hardware vsync events by returning
+ // true from addPresentFence() and addResyncSample().
+ void setIgnorePresentFences(bool ignore) override;
+
+ // dump appends human-readable debug info to the result string.
+ void dump(String8& result) const override;
+
+private:
+ void updateModelLocked();
+ void updateErrorLocked();
+ void resetLocked();
+ void resetErrorLocked();
+
+ enum { MAX_RESYNC_SAMPLES = 32 };
+ enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };
+ enum { NUM_PRESENT_SAMPLES = 8 };
+ enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
+ enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 };
+
+ const char* const mName;
+
+ // mPeriod is the computed period of the modeled vsync events in
+ // nanoseconds.
+ nsecs_t mPeriod;
+ nsecs_t mPeriodBase;
+
+ // mPhase is the phase offset of the modeled vsync events. It is the
+ // number of nanoseconds from time 0 to the first vsync event.
+ nsecs_t mPhase;
+
+ // mReferenceTime is the reference time of the modeled vsync events.
+ // It is the nanosecond timestamp of the first vsync event after a resync.
+ nsecs_t mReferenceTime;
+
+ // mError is the computed model error. It is based on the difference
+ // between the estimated vsync event times and those observed in the
+ // mPresentFences array.
+ nsecs_t mError;
+
+ // mZeroErrSamplesCount keeps track of how many times in a row there were
+ // zero timestamps available in the mPresentFences array.
+ // Used to sanity check that we are able to calculate the model error.
+ size_t mZeroErrSamplesCount;
+
+ // Whether we have updated the vsync event model since the last resync.
+ bool mModelUpdated;
+
+ // These member variables are the state used during the resynchronization
+ // process to store information about the hardware vsync event times used
+ // to compute the model.
+ nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+ size_t mFirstResyncSample;
+ size_t mNumResyncSamples;
+ int mNumResyncSamplesSincePresent;
+
+ // These member variables store information about the present fences used
+ // to validate the currently computed model.
+ std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE};
+ size_t mPresentSampleOffset;
+
+ int mRefreshSkipCount;
+
+ // mThread is the thread from which all the callbacks are called.
+ sp<DispSyncThread> mThread;
+
+ // mMutex is used to protect access to all member variables.
+ mutable Mutex mMutex;
+
+ // This is the offset from the present fence timestamps to the corresponding
+ // vsync event.
+ int64_t mPresentTimeOffset;
+
+ // Ignore present (retire) fences if the device doesn't have support for the
+ // sync framework
+ bool mIgnorePresentFences;
+
+ std::unique_ptr<Callback> mZeroPhaseTracer;
+};
+
+} // namespace impl
+
+} // namespace android
+
+#endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
new file mode 100644
index 0000000..fb6cff5
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <cutils/sched_policy.h>
+#include <log/log.h>
+#include <system/thread_defs.h>
+
+#include "EventControlThread.h"
+
+namespace android {
+
+EventControlThread::~EventControlThread() = default;
+
+namespace impl {
+
+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);
+}
+
+EventControlThread::~EventControlThread() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mKeepRunning = false;
+ mCondition.notify_all();
+ }
+ mThread.join();
+}
+
+void EventControlThread::setVsyncEnabled(bool enabled) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVsyncEnabled = enabled;
+ mCondition.notify_all();
+}
+
+// Unfortunately std::unique_lock gives warnings with -Wthread-safety
+void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
+ auto keepRunning = true;
+ auto currentVsyncEnabled = false;
+
+ while (keepRunning) {
+ mSetVSyncEnabled(currentVsyncEnabled);
+
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
+ return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
+ });
+ currentVsyncEnabled = mVsyncEnabled;
+ keepRunning = mKeepRunning;
+ }
+}
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h
new file mode 100644
index 0000000..cafae53
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventControlThread.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <cstddef>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+class EventControlThread {
+public:
+ virtual ~EventControlThread();
+
+ virtual void setVsyncEnabled(bool enabled) = 0;
+};
+
+namespace impl {
+
+class EventControlThread final : public android::EventControlThread {
+public:
+ using SetVSyncEnabledFunction = std::function<void(bool)>;
+
+ explicit EventControlThread(SetVSyncEnabledFunction function);
+ ~EventControlThread();
+
+ // EventControlThread implementation
+ void setVsyncEnabled(bool enabled) override;
+
+private:
+ void threadMain();
+
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+
+ const SetVSyncEnabledFunction mSetVSyncEnabled;
+ bool mVsyncEnabled GUARDED_BY(mMutex) = false;
+ bool mKeepRunning GUARDED_BY(mMutex) = true;
+
+ // Must be last so that everything is initialized before the thread starts.
+ std::thread mThread{&EventControlThread::threadMain, this};
+};
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
new file mode 100644
index 0000000..b84177c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <pthread.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <chrono>
+#include <cstdint>
+
+#include <cutils/compiler.h>
+#include <cutils/sched_policy.h>
+
+#include <gui/DisplayEventReceiver.h>
+
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#include "EventThread.h"
+
+using namespace std::chrono_literals;
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+EventThread::~EventThread() = default;
+
+namespace impl {
+
+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::~EventThread() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mKeepRunning = false;
+ mCondition.notify_all();
+ }
+ mThread.join();
+}
+
+void EventThread::setPhaseOffset(nsecs_t phaseOffset) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mVSyncSource->setPhaseOffset(phaseOffset);
+}
+
+sp<BnDisplayEventConnection> EventThread::createEventConnection() const {
+ return new Connection(const_cast<EventThread*>(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;
+}
+
+void EventThread::removeDisplayEventConnectionLocked(
+ const wp<EventThread::Connection>& connection) {
+ mDisplayEventConnections.remove(connection);
+}
+
+void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection>& connection) {
+ if (int32_t(count) >= 0) { // server must protect against bad params
+ std::lock_guard<std::mutex> lock(mMutex);
+ const int32_t new_count = (count == 0) ? -1 : count;
+ if (connection->count != new_count) {
+ connection->count = new_count;
+ mCondition.notify_all();
+ }
+ }
+}
+
+void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (mResyncWithRateLimitCallback) {
+ mResyncWithRateLimitCallback();
+ }
+
+ if (connection->count < 0) {
+ connection->count = 0;
+ mCondition.notify_all();
+ }
+}
+
+void EventThread::onScreenReleased() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mUseSoftwareVSync) {
+ // disable reliance on h/w vsync
+ mUseSoftwareVSync = true;
+ mCondition.notify_all();
+ }
+}
+
+void EventThread::onScreenAcquired() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mUseSoftwareVSync) {
+ // resume use of h/w vsync
+ mUseSoftwareVSync = false;
+ mCondition.notify_all();
+ }
+}
+
+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();
+}
+
+void EventThread::onHotplugReceived(DisplayType displayType, bool connected) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ DisplayEventReceiver::Event event;
+ event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG;
+ event.header.id = displayType == DisplayType::Primary ? 0 : 1;
+ event.header.timestamp = systemTime();
+ event.hotplug.connected = connected;
+
+ mPendingEvents.add(event);
+ mCondition.notify_all();
+}
+
+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);
+ if (err == -EAGAIN || err == -EWOULDBLOCK) {
+ // The destination doesn't accept events anymore, it's probably
+ // full. For now, we just drop the events on the floor.
+ // FIXME: Note that some events cannot be dropped and would have
+ // to be re-sent later.
+ // Right-now we don't have the ability to do this.
+ ALOGW("EventThread: dropping event (%08x) for connection %p", event.header.type,
+ conn.get());
+ } else if (err < 0) {
+ // handle any other error on the pipe as fatal. the only
+ // reasonable thing to do is to clean-up this connection.
+ // The most common error we'll get here is -EPIPE.
+ removeDisplayEventConnectionLocked(signalConnections[i]);
+ }
+ }
+ }
+}
+
+// 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* outEvent) {
+ Vector<sp<EventThread::Connection> > signalConnections;
+
+ while (signalConnections.isEmpty() && mKeepRunning) {
+ bool eventPending = false;
+ bool waitForVSync = false;
+
+ size_t vsyncCount = 0;
+ nsecs_t timestamp = 0;
+ for (auto& event : mVSyncEvent) {
+ timestamp = event.header.timestamp;
+ if (timestamp) {
+ // we have a vsync event to dispatch
+ if (mInterceptVSyncsCallback) {
+ mInterceptVSyncsCallback(timestamp);
+ }
+ *outEvent = event;
+ event.header.timestamp = 0;
+ vsyncCount = event.vsync.count;
+ break;
+ }
+ }
+
+ if (!timestamp) {
+ // no vsync event, see if there are some other event
+ eventPending = !mPendingEvents.isEmpty();
+ if (eventPending) {
+ // we have some other event to dispatch
+ *outEvent = mPendingEvents[0];
+ mPendingEvents.removeAt(0);
+ }
+ }
+
+ // find out connections waiting for events
+ size_t count = mDisplayEventConnections.size();
+ for (size_t i = 0; i < count;) {
+ sp<Connection> connection(mDisplayEventConnections[i].promote());
+ if (connection != nullptr) {
+ bool added = false;
+ if (connection->count >= 0) {
+ // we need vsync events because at least
+ // one connection is waiting for it
+ waitForVSync = true;
+ if (timestamp) {
+ // we consume the event only if it's time
+ // (ie: we received a vsync event)
+ if (connection->count == 0) {
+ // fired this time around
+ connection->count = -1;
+ signalConnections.add(connection);
+ added = true;
+ } else if (connection->count == 1 ||
+ (vsyncCount % connection->count) == 0) {
+ // continuous event, and time to report it
+ signalConnections.add(connection);
+ added = true;
+ }
+ }
+ }
+
+ if (eventPending && !timestamp && !added) {
+ // we don't have a vsync event to process
+ // (timestamp==0), but we have some pending
+ // messages.
+ signalConnections.add(connection);
+ }
+ ++i;
+ } else {
+ // we couldn't promote this reference, the connection has
+ // died, so clean-up!
+ mDisplayEventConnections.removeAt(i);
+ --count;
+ }
+ }
+
+ // Here we figure out if we need to enable or disable vsyncs
+ if (timestamp && !waitForVSync) {
+ // we received a VSYNC but we have no clients
+ // don't report it, and disable VSYNC events
+ disableVSyncLocked();
+ } 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();
+ }
+
+ // note: !timestamp implies signalConnections.isEmpty(), because we
+ // don't populate signalConnections if there's no vsync pending
+ if (!timestamp && !eventPending) {
+ // wait for something to happen
+ if (waitForVSync) {
+ // This is where we spend most of our time, waiting
+ // for vsync events and new client registrations.
+ //
+ // If the screen is off, we can't use h/w vsync, so we
+ // use a 16ms timeout instead. It doesn't need to be
+ // precise, we just need to keep feeding our clients.
+ //
+ // We don't want to stall if there's a driver bug, so we
+ // use a (long) timeout when waiting for h/w vsync, and
+ // generate fake events when necessary.
+ bool softwareSync = mUseSoftwareVSync;
+ auto timeout = softwareSync ? 16ms : 1000ms;
+ if (mCondition.wait_for(*lock, timeout) == std::cv_status::timeout) {
+ if (!softwareSync) {
+ ALOGW("Timed out waiting for hw vsync; faking it");
+ }
+ // FIXME: how do we decide which display id the fake
+ // vsync came from ?
+ mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ mVSyncEvent[0].header.id = 0;
+ mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ mVSyncEvent[0].vsync.count++;
+ }
+ } else {
+ // Nobody is interested in vsync, so we just want to sleep.
+ // h/w vsync should be disabled, so this will wait until we
+ // get a new connection, or an existing connection becomes
+ // interested in receiving vsync again.
+ mCondition.wait(*lock);
+ }
+ }
+ }
+
+ // here we're guaranteed to have a timestamp and some connections to signal
+ // (The connections might have dropped out of mDisplayEventConnections
+ // while we were asleep, but we'll still have strong references to them.)
+ return signalConnections;
+}
+
+void EventThread::enableVSyncLocked() {
+ if (!mUseSoftwareVSync) {
+ // never enable h/w VSYNC when screen is off
+ if (!mVsyncEnabled) {
+ mVsyncEnabled = true;
+ mVSyncSource->setCallback(this);
+ mVSyncSource->setVSyncEnabled(true);
+ }
+ }
+ mDebugVsyncEnabled = true;
+}
+
+void EventThread::disableVSyncLocked() {
+ if (mVsyncEnabled) {
+ mVsyncEnabled = false;
+ mVSyncSource->setVSyncEnabled(false);
+ mDebugVsyncEnabled = false;
+ }
+}
+
+void EventThread::dump(String8& result) const {
+ std::lock_guard<std::mutex> lock(mMutex);
+ result.appendFormat("VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled");
+ result.appendFormat(" soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled");
+ result.appendFormat(" numListeners=%zu,\n events-delivered: %u\n",
+ mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count);
+ for (size_t i = 0; i < mDisplayEventConnections.size(); i++) {
+ sp<Connection> connection = mDisplayEventConnections.itemAt(i).promote();
+ result.appendFormat(" %p: count=%d\n", connection.get(),
+ connection != nullptr ? connection->count : 0);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+EventThread::Connection::Connection(EventThread* eventThread)
+ : count(-1), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {}
+
+EventThread::Connection::~Connection() {
+ // do nothing here -- clean-up will happen automatically
+ // when the main thread wakes up
+}
+
+void EventThread::Connection::onFirstRef() {
+ // NOTE: mEventThread doesn't hold a strong reference on us
+ mEventThread->registerDisplayEventConnection(this);
+}
+
+status_t EventThread::Connection::stealReceiveChannel(gui::BitTube* outChannel) {
+ outChannel->setReceiveFd(mChannel.moveReceiveFd());
+ return NO_ERROR;
+}
+
+status_t EventThread::Connection::setVsyncRate(uint32_t count) {
+ mEventThread->setVsyncRate(count, this);
+ return NO_ERROR;
+}
+
+void EventThread::Connection::requestNextVsync() {
+ mEventThread->requestNextVsync(this);
+}
+
+status_t EventThread::Connection::postEvent(const DisplayEventReceiver::Event& event) {
+ ssize_t size = DisplayEventReceiver::sendEvents(&mChannel, &event, 1);
+ return size < 0 ? status_t(size) : status_t(NO_ERROR);
+}
+
+// ---------------------------------------------------------------------------
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
new file mode 100644
index 0000000..a0262b2
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <array>
+#include <condition_variable>
+#include <cstdint>
+#include <mutex>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
+
+#include <utils/Errors.h>
+#include <utils/SortedVector.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class EventThreadTest;
+class SurfaceFlinger;
+class String8;
+
+// ---------------------------------------------------------------------------
+
+class VSyncSource {
+public:
+ class Callback {
+ public:
+ virtual ~Callback() {}
+ virtual void onVSyncEvent(nsecs_t when) = 0;
+ };
+
+ virtual ~VSyncSource() {}
+ virtual void setVSyncEnabled(bool enable) = 0;
+ virtual void setCallback(Callback* callback) = 0;
+ virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+};
+
+class EventThread {
+public:
+ // TODO: Remove once stable display IDs are plumbed through SF/WM interface.
+ enum class DisplayType { Primary, External };
+
+ virtual ~EventThread();
+
+ virtual sp<BnDisplayEventConnection> createEventConnection() const = 0;
+
+ // called before the screen is turned off from main thread
+ virtual void onScreenReleased() = 0;
+
+ // called after the screen is turned on from main thread
+ virtual void onScreenAcquired() = 0;
+
+ // called when receiving a hotplug event
+ virtual void onHotplugReceived(DisplayType displayType, bool connected) = 0;
+
+ virtual void dump(String8& result) const = 0;
+
+ virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+};
+
+namespace impl {
+
+class EventThread : public android::EventThread, private VSyncSource::Callback {
+ class Connection : public BnDisplayEventConnection {
+ public:
+ explicit Connection(EventThread* eventThread);
+ virtual ~Connection();
+
+ virtual status_t postEvent(const DisplayEventReceiver::Event& event);
+
+ // count >= 1 : continuous event. count is the vsync rate
+ // count == 0 : one-shot event that has not fired
+ // count ==-1 : one-shot event that fired this round / disabled
+ int32_t count;
+
+ private:
+ virtual void onFirstRef();
+ status_t stealReceiveChannel(gui::BitTube* outChannel) override;
+ status_t setVsyncRate(uint32_t count) override;
+ void requestNextVsync() override; // asynchronous
+ EventThread* const mEventThread;
+ gui::BitTube mChannel;
+ };
+
+public:
+ using ResyncWithRateLimitCallback = std::function<void()>;
+ using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
+
+ EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback,
+ InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName);
+ ~EventThread();
+
+ sp<BnDisplayEventConnection> createEventConnection() const override;
+ status_t registerDisplayEventConnection(const sp<Connection>& connection);
+
+ void setVsyncRate(uint32_t count, const sp<Connection>& connection);
+ void requestNextVsync(const sp<Connection>& connection);
+
+ // called before the screen is turned off from main thread
+ void onScreenReleased() override;
+
+ // called after the screen is turned on from main thread
+ void onScreenAcquired() override;
+
+ // called when receiving a hotplug event
+ void onHotplugReceived(DisplayType displayType, bool connected) override;
+
+ void dump(String8& result) const override;
+
+ void setPhaseOffset(nsecs_t phaseOffset) override;
+
+private:
+ friend EventThreadTest;
+
+ void threadMain();
+ Vector<sp<EventThread::Connection>> waitForEventLocked(std::unique_lock<std::mutex>* lock,
+ DisplayEventReceiver::Event* event)
+ REQUIRES(mMutex);
+
+ void removeDisplayEventConnectionLocked(const wp<Connection>& connection) REQUIRES(mMutex);
+ void enableVSyncLocked() REQUIRES(mMutex);
+ void disableVSyncLocked() REQUIRES(mMutex);
+
+ // Implements VSyncSource::Callback
+ void onVSyncEvent(nsecs_t timestamp) override;
+
+ // constants
+ VSyncSource* const mVSyncSource GUARDED_BY(mMutex) = nullptr;
+ const ResyncWithRateLimitCallback mResyncWithRateLimitCallback;
+ const InterceptVSyncsCallback mInterceptVSyncsCallback;
+
+ std::thread mThread;
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mCondition;
+
+ // protected by mLock
+ SortedVector<wp<Connection>> mDisplayEventConnections GUARDED_BY(mMutex);
+ Vector<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+ std::array<DisplayEventReceiver::Event, 2> mVSyncEvent GUARDED_BY(mMutex);
+ bool mUseSoftwareVSync GUARDED_BY(mMutex) = false;
+ bool mVsyncEnabled GUARDED_BY(mMutex) = false;
+ bool mKeepRunning GUARDED_BY(mMutex) = true;
+
+ // for debugging
+ bool mDebugVsyncEnabled GUARDED_BY(mMutex) = false;
+};
+
+// ---------------------------------------------------------------------------
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
new file mode 100644
index 0000000..056d381
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+
+#include "EventThread.h"
+#include "MessageQueue.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+MessageBase::MessageBase() : MessageHandler() {}
+
+MessageBase::~MessageBase() {}
+
+void MessageBase::handleMessage(const Message&) {
+ this->handler();
+ barrier.open();
+};
+
+// ---------------------------------------------------------------------------
+
+MessageQueue::~MessageQueue() = default;
+
+// ---------------------------------------------------------------------------
+
+namespace impl {
+
+void MessageQueue::Handler::dispatchRefresh() {
+ if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
+ mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
+ }
+}
+
+void MessageQueue::Handler::dispatchInvalidate() {
+ if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
+ mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
+ }
+}
+
+void MessageQueue::Handler::handleMessage(const Message& message) {
+ switch (message.what) {
+ case INVALIDATE:
+ android_atomic_and(~eventMaskInvalidate, &mEventMask);
+ mQueue.mFlinger->onMessageReceived(message.what);
+ break;
+ case REFRESH:
+ android_atomic_and(~eventMaskRefresh, &mEventMask);
+ mQueue.mFlinger->onMessageReceived(message.what);
+ break;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+void MessageQueue::init(const sp<SurfaceFlinger>& flinger) {
+ mFlinger = flinger;
+ mLooper = new Looper(true);
+ mHandler = new Handler(*this);
+}
+
+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);
+}
+
+void MessageQueue::waitMessage() {
+ do {
+ IPCThreadState::self()->flushCommands();
+ int32_t ret = mLooper->pollOnce(-1);
+ switch (ret) {
+ case Looper::POLL_WAKE:
+ case Looper::POLL_CALLBACK:
+ continue;
+ case Looper::POLL_ERROR:
+ ALOGE("Looper::POLL_ERROR");
+ continue;
+ case Looper::POLL_TIMEOUT:
+ // timeout (should not happen)
+ continue;
+ default:
+ // should not happen
+ ALOGE("Looper::pollOnce() returned unknown status %d", ret);
+ continue;
+ }
+ } while (true);
+}
+
+status_t MessageQueue::postMessage(const sp<MessageBase>& messageHandler, nsecs_t relTime) {
+ const Message dummyMessage;
+ if (relTime > 0) {
+ mLooper->sendMessageDelayed(relTime, messageHandler, dummyMessage);
+ } else {
+ mLooper->sendMessage(messageHandler, dummyMessage);
+ }
+ return NO_ERROR;
+}
+
+void MessageQueue::invalidate() {
+ mEvents->requestNextVsync();
+}
+
+void MessageQueue::refresh() {
+ mHandler->dispatchRefresh();
+}
+
+int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
+ MessageQueue* queue = reinterpret_cast<MessageQueue*>(data);
+ return queue->eventReceiver(fd, events);
+}
+
+int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) {
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[8];
+ while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) {
+ for (int i = 0; i < n; i++) {
+ if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+ mHandler->dispatchInvalidate();
+ break;
+ }
+ }
+ }
+ return 1;
+}
+
+// ---------------------------------------------------------------------------
+
+} // namespace impl
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
new file mode 100644
index 0000000..90d1c72
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MESSAGE_QUEUE_H
+#define ANDROID_MESSAGE_QUEUE_H
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Looper.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
+
+#include "Barrier.h"
+
+#include <functional>
+
+namespace android {
+
+class EventThread;
+class SurfaceFlinger;
+
+// ---------------------------------------------------------------------------
+
+class MessageBase : public MessageHandler {
+public:
+ MessageBase();
+
+ // return true if message has a handler
+ virtual bool handler() = 0;
+
+ // waits for the handler to be processed
+ void wait() const { barrier.wait(); }
+
+protected:
+ virtual ~MessageBase();
+
+private:
+ virtual void handleMessage(const Message& message);
+
+ mutable Barrier barrier;
+};
+
+class LambdaMessage : public MessageBase {
+public:
+ explicit LambdaMessage(std::function<void()> handler)
+ : MessageBase(), mHandler(std::move(handler)) {}
+
+ bool handler() override {
+ mHandler();
+ // This return value is no longer checked, so it's always safe to return true
+ return true;
+ }
+
+private:
+ const std::function<void()> mHandler;
+};
+
+// ---------------------------------------------------------------------------
+
+class MessageQueue {
+public:
+ enum {
+ INVALIDATE = 0,
+ REFRESH = 1,
+ };
+
+ virtual ~MessageQueue();
+
+ virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
+ virtual void setEventThread(EventThread* events) = 0;
+ virtual void waitMessage() = 0;
+ virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
+ virtual void invalidate() = 0;
+ virtual void refresh() = 0;
+};
+
+// ---------------------------------------------------------------------------
+
+namespace impl {
+
+class MessageQueue final : public android::MessageQueue {
+ class Handler : public MessageHandler {
+ enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 };
+ MessageQueue& mQueue;
+ int32_t mEventMask;
+
+ public:
+ explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {}
+ virtual void handleMessage(const Message& message);
+ void dispatchRefresh();
+ void dispatchInvalidate();
+ };
+
+ friend class Handler;
+
+ sp<SurfaceFlinger> mFlinger;
+ sp<Looper> mLooper;
+ android::EventThread* mEventThread;
+ sp<IDisplayEventConnection> mEvents;
+ gui::BitTube mEventTube;
+ sp<Handler> mHandler;
+
+ static int cb_eventReceiver(int fd, int events, void* data);
+ int eventReceiver(int fd, int events);
+
+public:
+ ~MessageQueue() override = default;
+ void init(const sp<SurfaceFlinger>& flinger) override;
+ void setEventThread(android::EventThread* events) override;
+
+ void waitMessage() override;
+ status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override;
+
+ // sends INVALIDATE message at next VSYNC
+ void invalidate() override;
+ // sends REFRESH message at next VSYNC
+ void refresh() override;
+};
+
+// ---------------------------------------------------------------------------
+
+} // namespace impl
+} // namespace android
+
+#endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
new file mode 100644
index 0000000..7dfad43
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Errors.h>
+
+#include <mutex>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+/*
+ * Modulates the vsync-offsets depending on current SurfaceFlinger state.
+ */
+class VSyncModulator {
+private:
+ // Number of frames we'll keep the early phase offsets once they are activated. This acts as a
+ // low-pass filter in case the client isn't quick enough in sending new transactions.
+ const int MIN_EARLY_FRAME_COUNT = 2;
+
+public:
+ struct Offsets {
+ nsecs_t sf;
+ nsecs_t app;
+ };
+
+ enum TransactionStart { EARLY, NORMAL };
+
+ // Sets the phase offsets
+ //
+ // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
+ // as early. May be the same as late, in which case we don't shift offsets.
+ // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
+ // and the transaction was marked as early, we'll use sfEarly.
+ // sfLate: The regular SF vsync phase offset.
+ // appEarly: Like sfEarly, but for the app-vsync
+ // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
+ // appLate: The regular app vsync phase offset.
+ void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) {
+ mEarlyOffsets = early;
+ mEarlyGlOffsets = earlyGl;
+ mLateOffsets = late;
+ mOffsets = late;
+ }
+
+ Offsets getEarlyOffsets() const { return mEarlyOffsets; }
+
+ Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; }
+
+ void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
+ mSfEventThread = sfEventThread;
+ mAppEventThread = appEventThread;
+ }
+
+ void setTransactionStart(TransactionStart transactionStart) {
+ if (transactionStart == TransactionStart::EARLY) {
+ mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT;
+ }
+
+ // An early transaction stays an early transaction.
+ if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) {
+ return;
+ }
+ mTransactionStart = transactionStart;
+ updateOffsets();
+ }
+
+ void onTransactionHandled() {
+ if (mTransactionStart == TransactionStart::NORMAL) return;
+ mTransactionStart = TransactionStart::NORMAL;
+ updateOffsets();
+ }
+
+ void onRefreshed(bool usedRenderEngine) {
+ bool updateOffsetsNeeded = false;
+ if (mRemainingEarlyFrameCount > 0) {
+ mRemainingEarlyFrameCount--;
+ updateOffsetsNeeded = true;
+ }
+ if (usedRenderEngine != mLastFrameUsedRenderEngine) {
+ mLastFrameUsedRenderEngine = usedRenderEngine;
+ updateOffsetsNeeded = true;
+ }
+ if (updateOffsetsNeeded) {
+ updateOffsets();
+ }
+ }
+
+private:
+ void updateOffsets() {
+ const Offsets desired = getOffsets();
+ const Offsets current = mOffsets;
+
+ bool changed = false;
+ if (desired.sf != current.sf) {
+ mSfEventThread->setPhaseOffset(desired.sf);
+ changed = true;
+ }
+ if (desired.app != current.app) {
+ mAppEventThread->setPhaseOffset(desired.app);
+ changed = true;
+ }
+
+ if (changed) {
+ mOffsets = desired;
+ }
+ }
+
+ Offsets getOffsets() {
+ if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) {
+ return mEarlyOffsets;
+ } else if (mLastFrameUsedRenderEngine) {
+ return mEarlyGlOffsets;
+ } else {
+ return mLateOffsets;
+ }
+ }
+
+ Offsets mLateOffsets;
+ Offsets mEarlyOffsets;
+ Offsets mEarlyGlOffsets;
+
+ EventThread* mSfEventThread = nullptr;
+ EventThread* mAppEventThread = nullptr;
+
+ std::atomic<Offsets> mOffsets;
+
+ std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
+ std::atomic<bool> mLastFrameUsedRenderEngine = false;
+ std::atomic<int> mRemainingEarlyFrameCount = 0;
+};
+
+} // namespace android