summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/surfaceflinger/Android.mk18
-rw-r--r--services/surfaceflinger/DispSync.cpp482
-rw-r--r--services/surfaceflinger/DispSync.h149
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp2
-rw-r--r--services/surfaceflinger/EventThread.cpp42
-rw-r--r--services/surfaceflinger/EventThread.h26
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp173
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h12
8 files changed, 863 insertions, 41 deletions
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 7cc4ce1..bd89bd5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -4,6 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
Client.cpp \
DisplayDevice.cpp \
+ DispSync.cpp \
EventThread.cpp \
FrameTracker.cpp \
Layer.cpp \
@@ -53,6 +54,23 @@ ifneq ($(NUM_FRAMEBUFFER_SURFACE_BUFFERS),)
LOCAL_CFLAGS += -DNUM_FRAMEBUFFER_SURFACE_BUFFERS=$(NUM_FRAMEBUFFER_SURFACE_BUFFERS)
endif
+ifeq ($(TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK),true)
+ LOCAL_CFLAGS += -DRUNNING_WITHOUT_SYNC_FRAMEWORK
+endif
+
+# See build/target/board/generic/BoardConfig.mk for a description of this setting.
+ifneq ($(VSYNC_EVENT_PHASE_OFFSET_NS),)
+ LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=$(VSYNC_EVENT_PHASE_OFFSET_NS)
+else
+ LOCAL_CFLAGS += -DVSYNC_EVENT_PHASE_OFFSET_NS=0
+endif
+
+ifneq ($(PRESENT_TIME_OFFSET_FROM_VSYNC_NS),)
+ LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=$(PRESENT_TIME_OFFSET_FROM_VSYNC_NS)
+else
+ LOCAL_CFLAGS += -DPRESENT_TIME_OFFSET_FROM_VSYNC_NS=0
+endif
+
LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_SHARED_LIBRARIES := \
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
new file mode 100644
index 0000000..7e67138
--- /dev/null
+++ b/services/surfaceflinger/DispSync.cpp
@@ -0,0 +1,482 @@
+/*
+ * 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
+
+// This is needed for stdint.h to define INT64_MAX in C++
+#define __STDC_LIMIT_MACROS
+
+#include <math.h>
+
+#include <cutils/log.h>
+
+#include <ui/Fence.h>
+
+#include <utils/String8.h>
+#include <utils/Thread.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#include "DispSync.h"
+#include "EventLog/EventLog.h"
+
+namespace android {
+
+// Setting this to true enables verbose tracing that can be used to debug
+// vsync event model or phase issues.
+static const bool traceDetailedInfo = 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 errorThreshold = 160000000000;
+
+// This works around the lack of support for the sync framework on some
+// devices.
+#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK
+static const bool runningWithoutSyncFramework = true;
+#else
+static const bool runningWithoutSyncFramework = false;
+#endif
+
+// This is the offset from the present fence timestamps to the corresponding
+// vsync event.
+static const int64_t presentTimeOffset = PRESENT_TIME_OFFSET_FROM_VSYNC_NS;
+
+class DispSyncThread: public Thread {
+public:
+
+ DispSyncThread():
+ mStop(false),
+ mPeriod(0),
+ mPhase(0),
+ mWakeupLatency(0) {
+ }
+
+ virtual ~DispSyncThread() {}
+
+ void updateModel(nsecs_t period, nsecs_t phase) {
+ Mutex::Autolock lock(mMutex);
+ mPeriod = period;
+ mPhase = phase;
+ mCond.signal();
+ }
+
+ void stop() {
+ Mutex::Autolock lock(mMutex);
+ mStop = true;
+ mCond.signal();
+ }
+
+ virtual bool threadLoop() {
+ status_t err;
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t nextEventTime = 0;
+
+ 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;
+ }
+
+ nextEventTime = computeNextEventTimeLocked(now);
+ targetTime = nextEventTime - mWakeupLatency;
+
+ bool isWakeup = false;
+
+ if (now < 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);
+
+ if (isWakeup) {
+ mWakeupLatency = ((mWakeupLatency * 63) +
+ (now - targetTime)) / 64;
+ if (mWakeupLatency > 500000) {
+ // Don't correct by more than 500 us
+ mWakeupLatency = 500000;
+ }
+ if (traceDetailedInfo) {
+ ATRACE_INT64("DispSync:WakeupLat", now - nextEventTime);
+ ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency);
+ }
+ }
+
+ callbackInvocations = gatherCallbackInvocationsLocked(now);
+ }
+
+ if (callbackInvocations.size() > 0) {
+ fireCallbackInvocations(callbackInvocations);
+ }
+ }
+
+ return false;
+ }
+
+ status_t addEventListener(nsecs_t phase, const sp<DispSync::Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ if (mEventListeners[i].mCallback == callback) {
+ return BAD_VALUE;
+ }
+ }
+
+ EventListener listener;
+ listener.mPhase = phase;
+ listener.mCallback = callback;
+ listener.mLastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mEventListeners.push(listener);
+
+ mCond.signal();
+
+ return NO_ERROR;
+ }
+
+ status_t removeEventListener(const sp<DispSync::Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ if (mEventListeners[i].mCallback == callback) {
+ mEventListeners.removeAt(i);
+ mCond.signal();
+ return NO_ERROR;
+ }
+ }
+
+ return BAD_VALUE;
+ }
+
+ // This method is only here to handle the runningWithoutSyncFramework
+ // case.
+ bool hasAnyEventListeners() {
+ Mutex::Autolock lock(mMutex);
+ return !mEventListeners.empty();
+ }
+
+private:
+
+ struct EventListener {
+ nsecs_t mPhase;
+ nsecs_t mLastEventTime;
+ sp<DispSync::Callback> mCallback;
+ };
+
+ struct CallbackInvocation {
+ sp<DispSync::Callback> mCallback;
+ nsecs_t mEventTime;
+ };
+
+ nsecs_t computeNextEventTimeLocked(nsecs_t now) {
+ 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;
+ }
+ }
+
+ return nextEventTime;
+ }
+
+ Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
+ Vector<CallbackInvocation> callbackInvocations;
+ nsecs_t ref = now - mPeriod;
+
+ for (size_t i = 0; i < mEventListeners.size(); i++) {
+ nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i],
+ ref);
+
+ if (t - mWakeupLatency < now) {
+ CallbackInvocation ci;
+ ci.mCallback = mEventListeners[i].mCallback;
+ ci.mEventTime = t;
+ callbackInvocations.push(ci);
+ mEventListeners.editItemAt(i).mLastEventTime = t;
+ }
+ }
+
+ return callbackInvocations;
+ }
+
+ nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener,
+ nsecs_t ref) {
+
+ nsecs_t lastEventTime = listener.mLastEventTime;
+ if (ref < lastEventTime) {
+ ref = lastEventTime;
+ }
+
+ nsecs_t phase = mPhase + listener.mPhase;
+ nsecs_t t = (((ref - phase) / mPeriod) + 1) * mPeriod + phase;
+
+ if (t - listener.mLastEventTime < mPeriod / 2) {
+ t += mPeriod;
+ }
+
+ return t;
+ }
+
+ void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) {
+ for (size_t i = 0; i < callbacks.size(); i++) {
+ callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
+ }
+ }
+
+ bool mStop;
+
+ nsecs_t mPeriod;
+ nsecs_t mPhase;
+ nsecs_t mWakeupLatency;
+
+ Vector<EventListener> mEventListeners;
+
+ Mutex mMutex;
+ Condition mCond;
+};
+
+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() {
+ mThread = new DispSyncThread();
+ mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+
+ reset();
+ beginResync();
+
+ if (traceDetailedInfo) {
+ // If runningWithoutSyncFramework is true then the ZeroPhaseTracer
+ // would prevent HW vsync event from ever being turned off.
+ // Furthermore the zero-phase tracing is not needed because any time
+ // there is an event registered we will turn on the HW vsync events.
+ if (!runningWithoutSyncFramework) {
+ addEventListener(0, new ZeroPhaseTracer());
+ }
+ }
+}
+
+DispSync::~DispSync() {}
+
+void DispSync::reset() {
+ Mutex::Autolock lock(mMutex);
+
+ mNumResyncSamples = 0;
+ mFirstResyncSample = 0;
+ mNumResyncSamplesSincePresent = 0;
+ resetErrorLocked();
+}
+
+bool DispSync::addPresentFence(const sp<Fence>& fence) {
+ Mutex::Autolock lock(mMutex);
+
+ mPresentFences[mPresentSampleOffset] = fence;
+ mPresentTimes[mPresentSampleOffset] = 0;
+ mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
+ mNumResyncSamplesSincePresent = 0;
+
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ const sp<Fence>& f(mPresentFences[i]);
+ if (f != NULL) {
+ nsecs_t t = f->getSignalTime();
+ if (t < INT64_MAX) {
+ mPresentFences[i].clear();
+ mPresentTimes[i] = t + presentTimeOffset;
+ }
+ }
+ }
+
+ updateErrorLocked();
+
+ return mPeriod == 0 || mError > errorThreshold;
+}
+
+void DispSync::beginResync() {
+ Mutex::Autolock lock(mMutex);
+
+ mNumResyncSamples = 0;
+}
+
+bool DispSync::addResyncSample(nsecs_t timestamp) {
+ Mutex::Autolock lock(mMutex);
+
+ size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
+ mResyncSamples[idx] = timestamp;
+
+ if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
+ mNumResyncSamples++;
+ } else {
+ mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
+ }
+
+ updateModelLocked();
+
+ if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
+ resetErrorLocked();
+ }
+
+ if (runningWithoutSyncFramework) {
+ // If we don't have the sync framework we will never have
+ // addPresentFence called. This means 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 whenever we need to generate
+ // SW vsync events.
+ return mThread->hasAnyEventListeners();
+ }
+
+ return mPeriod == 0 || mError > errorThreshold;
+}
+
+void DispSync::endResync() {
+}
+
+status_t DispSync::addEventListener(nsecs_t phase,
+ const sp<Callback>& callback) {
+
+ Mutex::Autolock lock(mMutex);
+ return mThread->addEventListener(phase, callback);
+}
+
+status_t DispSync::removeEventListener(const sp<Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+ return mThread->removeEventListener(callback);
+}
+
+void DispSync::setPeriod(nsecs_t period) {
+ Mutex::Autolock lock(mMutex);
+ mPeriod = period;
+ mPhase = 0;
+}
+
+void DispSync::updateModelLocked() {
+ if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {
+ nsecs_t durationSum = 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;
+ durationSum += mResyncSamples[idx] - mResyncSamples[prev];
+ }
+
+ mPeriod = durationSum / (mNumResyncSamples - 1);
+
+ double sampleAvgX = 0;
+ double sampleAvgY = 0;
+ double scale = 2.0 * M_PI / double(mPeriod);
+ for (size_t i = 0; i < mNumResyncSamples; i++) {
+ size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
+ nsecs_t sample = mResyncSamples[idx];
+ double samplePhase = double(sample % mPeriod) * scale;
+ sampleAvgX += cos(samplePhase);
+ sampleAvgY += sin(samplePhase);
+ }
+
+ sampleAvgX /= double(mNumResyncSamples);
+ sampleAvgY /= double(mNumResyncSamples);
+
+ mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
+
+ if (mPhase < 0) {
+ mPhase += mPeriod;
+ }
+
+ if (traceDetailedInfo) {
+ ATRACE_INT64("DispSync:Period", mPeriod);
+ ATRACE_INT64("DispSync:Phase", mPhase);
+ }
+
+ mThread->updateModel(mPeriod, mPhase);
+ }
+}
+
+void DispSync::updateErrorLocked() {
+ if (mPeriod == 0) {
+ return;
+ }
+
+ int numErrSamples = 0;
+ nsecs_t sqErrSum = 0;
+
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ nsecs_t sample = mPresentTimes[i];
+ if (sample > mPhase) {
+ nsecs_t sampleErr = (sample - mPhase) % mPeriod;
+ if (sampleErr > mPeriod / 2) {
+ sampleErr -= mPeriod;
+ }
+ sqErrSum += sampleErr * sampleErr;
+ numErrSamples++;
+ }
+ }
+
+ if (numErrSamples > 0) {
+ mError = sqErrSum / numErrSamples;
+ } else {
+ mError = 0;
+ }
+
+ if (traceDetailedInfo) {
+ ATRACE_INT64("DispSync:Error", mError);
+ }
+}
+
+void DispSync::resetErrorLocked() {
+ mPresentSampleOffset = 0;
+ mError = 0;
+ for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) {
+ mPresentFences[i].clear();
+ mPresentTimes[i] = 0;
+ }
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
new file mode 100644
index 0000000..c4280aa
--- /dev/null
+++ b/services/surfaceflinger/DispSync.h
@@ -0,0 +1,149 @@
+/*
+ * 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/Timers.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class String8;
+class Fence;
+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:
+
+ class Callback: public virtual RefBase {
+ public:
+ virtual ~Callback() {};
+ virtual void onDispSyncEvent(nsecs_t when) = 0;
+ };
+
+ DispSync();
+ ~DispSync();
+
+ void reset();
+
+ // 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 sp<Fence>& fence);
+
+ // 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();
+ bool addResyncSample(nsecs_t timestamp);
+ void endResync();
+
+ // The setPreiod 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);
+
+ // 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(nsecs_t phase, const sp<Callback>& callback);
+
+ // 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(const sp<Callback>& callback);
+
+private:
+
+ void updateModelLocked();
+ void updateErrorLocked();
+ void resetErrorLocked();
+
+ enum { MAX_RESYNC_SAMPLES = 32 };
+ enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 3 };
+ enum { NUM_PRESENT_SAMPLES = 8 };
+ enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 12 };
+
+ // mPeriod is the computed period of the modeled vsync events in
+ // nanoseconds.
+ nsecs_t mPeriod;
+
+ // 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;
+
+ // mError is the computed model error. It is based on the difference
+ // between the estimated vsync event times and those observed in the
+ // mPresentTimes array.
+ nsecs_t mError;
+
+ // 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.
+ sp<Fence> mPresentFences[NUM_PRESENT_SAMPLES];
+ nsecs_t mPresentTimes[NUM_PRESENT_SAMPLES];
+ size_t mPresentSampleOffset;
+
+ // 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;
+};
+
+}
+
+#endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 8a80488..56b0f49 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -284,7 +284,7 @@ void HWComposer::invalidate() {
void HWComposer::vsync(int disp, int64_t timestamp) {
if (uint32_t(disp) < HWC_NUM_PHYSICAL_DISPLAY_TYPES) {
char tag[16];
- snprintf(tag, sizeof(tag), "VSYNC_%1u", disp);
+ snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp);
ATRACE_INT(tag, ++mVSyncCounts[disp] & 1);
mEventHandler.onVSyncReceived(disp, timestamp);
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index a61ad72..3528b62 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -36,9 +36,10 @@
namespace android {
// ---------------------------------------------------------------------------
-EventThread::EventThread(const sp<SurfaceFlinger>& flinger)
- : mFlinger(flinger),
+EventThread::EventThread(const sp<VSyncSource>& src)
+ : mVSyncSource(src),
mUseSoftwareVSync(false),
+ mVsyncEnabled(false),
mDebugVsyncEnabled(false) {
for (int32_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
@@ -110,19 +111,13 @@ void EventThread::onScreenAcquired() {
}
}
-
-void EventThread::onVSyncReceived(int type, nsecs_t timestamp) {
- ALOGE_IF(type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES,
- "received vsync event for an invalid display (id=%d)", type);
-
+void EventThread::onVSyncEvent(nsecs_t timestamp) {
Mutex::Autolock _l(mLock);
- if (type < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- mVSyncEvent[type].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
- mVSyncEvent[type].header.id = type;
- mVSyncEvent[type].header.timestamp = timestamp;
- mVSyncEvent[type].vsync.count++;
- mCondition.broadcast();
- }
+ mVSyncEvent[0].header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
+ mVSyncEvent[0].header.id = 0;
+ mVSyncEvent[0].header.timestamp = timestamp;
+ mVSyncEvent[0].vsync.count++;
+ mCondition.broadcast();
}
void EventThread::onHotplugReceived(int type, bool connected) {
@@ -308,18 +303,23 @@ Vector< sp<EventThread::Connection> > EventThread::waitForEvent(
void EventThread::enableVSyncLocked() {
if (!mUseSoftwareVSync) {
// never enable h/w VSYNC when screen is off
- mFlinger->eventControl(DisplayDevice::DISPLAY_PRIMARY,
- SurfaceFlinger::EVENT_VSYNC, true);
- mPowerHAL.vsyncHint(true);
+ if (!mVsyncEnabled) {
+ mVsyncEnabled = true;
+ mVSyncSource->setCallback(static_cast<VSyncSource::Callback*>(this));
+ mVSyncSource->setVSyncEnabled(true);
+ mPowerHAL.vsyncHint(true);
+ }
}
mDebugVsyncEnabled = true;
}
void EventThread::disableVSyncLocked() {
- mFlinger->eventControl(DisplayDevice::DISPLAY_PRIMARY,
- SurfaceFlinger::EVENT_VSYNC, false);
- mPowerHAL.vsyncHint(false);
- mDebugVsyncEnabled = false;
+ if (mVsyncEnabled) {
+ mVsyncEnabled = false;
+ mVSyncSource->setVSyncEnabled(false);
+ mPowerHAL.vsyncHint(false);
+ mDebugVsyncEnabled = false;
+ }
}
void EventThread::dump(String8& result) const {
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 5e88693..f6ab4a7 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -39,7 +39,21 @@ class String8;
// ---------------------------------------------------------------------------
-class EventThread : public Thread {
+
+class VSyncSource : public virtual RefBase {
+public:
+ class Callback: public virtual RefBase {
+ public:
+ virtual ~Callback() {}
+ virtual void onVSyncEvent(nsecs_t when) = 0;
+ };
+
+ virtual ~VSyncSource() {}
+ virtual void setVSyncEnabled(bool enable) = 0;
+ virtual void setCallback(const sp<Callback>& callback) = 0;
+};
+
+class EventThread : public Thread, private VSyncSource::Callback {
class Connection : public BnDisplayEventConnection {
public:
Connection(const sp<EventThread>& eventThread);
@@ -62,7 +76,7 @@ class EventThread : public Thread {
public:
- EventThread(const sp<SurfaceFlinger>& flinger);
+ EventThread(const sp<VSyncSource>& src);
sp<Connection> createEventConnection() const;
status_t registerDisplayEventConnection(const sp<Connection>& connection);
@@ -76,8 +90,7 @@ public:
// called after the screen is turned on from main thread
void onScreenAcquired();
- // called when receiving a vsync event
- void onVSyncReceived(int type, nsecs_t timestamp);
+ // called when receiving a hotplug event
void onHotplugReceived(int type, bool connected);
Vector< sp<EventThread::Connection> > waitForEvent(
@@ -89,12 +102,14 @@ private:
virtual bool threadLoop();
virtual void onFirstRef();
+ virtual void onVSyncEvent(nsecs_t timestamp);
+
void removeDisplayEventConnection(const wp<Connection>& connection);
void enableVSyncLocked();
void disableVSyncLocked();
// constants
- sp<SurfaceFlinger> mFlinger;
+ sp<VSyncSource> mVSyncSource;
PowerHAL mPowerHAL;
mutable Mutex mLock;
@@ -105,6 +120,7 @@ private:
Vector< DisplayEventReceiver::Event > mPendingEvents;
DisplayEventReceiver::Event mVSyncEvent[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES];
bool mUseSoftwareVSync;
+ bool mVsyncEnabled;
// for debugging
bool mDebugVsyncEnabled;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3ae335f..c025444 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -59,6 +59,7 @@
#include "Colorizer.h"
#include "DdmConnection.h"
#include "DisplayDevice.h"
+#include "DispSync.h"
#include "EventThread.h"
#include "Layer.h"
#include "LayerDim.h"
@@ -84,6 +85,37 @@
EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
namespace android {
+
+// This works around the lack of support for the sync framework on some
+// devices.
+#ifdef RUNNING_WITHOUT_SYNC_FRAMEWORK
+static const bool runningWithoutSyncFramework = true;
+#else
+static const bool runningWithoutSyncFramework = false;
+#endif
+
+// This is the phase offset in nanoseconds of the software vsync event
+// relative to the vsync event reported by HWComposer. The software vsync
+// event is when SurfaceFlinger and Choreographer-based applications run each
+// frame.
+//
+// This phase offset allows adjustment of the minimum latency from application
+// wake-up (by Choregographer) time to the time at which the resulting window
+// image is displayed. This value may be either positive (after the HW vsync)
+// or negative (before the HW vsync). Setting it to 0 will result in a
+// minimum latency of two vsync periods because the app and SurfaceFlinger
+// will run just after the HW vsync. Setting it to a positive number will
+// result in the minimum latency being:
+//
+// (2 * VSYNC_PERIOD - (vsyncPhaseOffsetNs % VSYNC_PERIOD))
+//
+// Note that reducing this latency makes it more likely for the applications
+// to not have their window content image ready in time. When this happens
+// the latency will end up being an additional vsync period, and animations
+// will hiccup. Therefore, this latency should be tuned somewhat
+// conservatively (or at least with awareness of the trade-off being made).
+static const int64_t vsyncPhaseOffsetNs = VSYNC_EVENT_PHASE_OFFSET_NS;
+
// ---------------------------------------------------------------------------
const String16 sHardwareTest("android.permission.HARDWARE_TEST");
@@ -114,6 +146,7 @@ SurfaceFlinger::SurfaceFlinger()
mDebugInTransaction(0),
mLastTransactionTime(0),
mBootFinished(false),
+ mPrimaryHWVsyncEnabled(false),
mDaltonize(false),
mHasColorMatrix(false)
{
@@ -403,8 +436,63 @@ status_t SurfaceFlinger::selectEGLConfig(EGLDisplay display, EGLint nativeVisual
return err;
}
-void SurfaceFlinger::init() {
+class DispSyncSource : public VSyncSource, private DispSync::Callback {
+public:
+ DispSyncSource(DispSync* dispSync) : mValue(0), mDispSync(dispSync) {}
+
+ virtual ~DispSyncSource() {}
+
+ virtual void setVSyncEnabled(bool enable) {
+ // Do NOT lock the mutex here so as to avoid any mutex ordering issues
+ // with locking it in the onDispSyncEvent callback.
+ if (enable) {
+ status_t err = mDispSync->addEventListener(vsyncPhaseOffsetNs,
+ static_cast<DispSync::Callback*>(this));
+ if (err != NO_ERROR) {
+ ALOGE("error registering vsync callback: %s (%d)",
+ strerror(-err), err);
+ }
+ ATRACE_INT("VsyncOn", 1);
+ } else {
+ status_t err = mDispSync->removeEventListener(
+ static_cast<DispSync::Callback*>(this));
+ if (err != NO_ERROR) {
+ ALOGE("error unregistering vsync callback: %s (%d)",
+ strerror(-err), err);
+ }
+ ATRACE_INT("VsyncOn", 0);
+ }
+ }
+
+ virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
+ Mutex::Autolock lock(mMutex);
+ mCallback = callback;
+ }
+
+private:
+ virtual void onDispSyncEvent(nsecs_t when) {
+ sp<VSyncSource::Callback> callback;
+ {
+ Mutex::Autolock lock(mMutex);
+ callback = mCallback;
+ mValue = (mValue + 1) % 2;
+ ATRACE_INT("VSYNC", mValue);
+ }
+
+ if (callback != NULL) {
+ callback->onVSyncEvent(when);
+ }
+ }
+
+ int mValue;
+
+ DispSync* mDispSync;
+ sp<VSyncSource::Callback> mCallback;
+ Mutex mMutex;
+};
+
+void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
@@ -500,9 +588,15 @@ void SurfaceFlinger::init() {
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
// start the EventThread
- mEventThread = new EventThread(this);
+ sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync);
+ mEventThread = new EventThread(vsyncSrc);
mEventQueue.setEventThread(mEventThread);
+ // set a fake vsync period if there is no HWComposer
+ if (mHwc->initCheck() != NO_ERROR) {
+ mPrimaryDispSync.setPeriod(16666667);
+ }
+
// initialize our drawing state
mDrawingState = mCurrentState;
@@ -657,17 +751,49 @@ void SurfaceFlinger::run() {
} while (true);
}
-void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
- if (mEventThread == NULL) {
- // This is a temporary workaround for b/7145521. A non-null pointer
- // does not mean EventThread has finished initializing, so this
- // is not a correct fix.
- ALOGW("WARNING: EventThread not started, ignoring vsync");
- return;
+void SurfaceFlinger::enableHardwareVsync() {
+ Mutex::Autolock _l(mHWVsyncLock);
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync.beginResync();
+ eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+ mPrimaryHWVsyncEnabled = true;
}
- if (uint32_t(type) < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
- // we should only receive DisplayDevice::DisplayType from the vsync callback
- mEventThread->onVSyncReceived(type, timestamp);
+}
+
+void SurfaceFlinger::resyncToHardwareVsync() {
+ Mutex::Autolock _l(mHWVsyncLock);
+
+ const nsecs_t period =
+ getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY);
+
+ mPrimaryDispSync.reset();
+ mPrimaryDispSync.setPeriod(period);
+
+ if (!mPrimaryHWVsyncEnabled) {
+ mPrimaryDispSync.beginResync();
+ eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true);
+ mPrimaryHWVsyncEnabled = true;
+ }
+}
+
+void SurfaceFlinger::disableHardwareVsync() {
+ Mutex::Autolock _l(mHWVsyncLock);
+ if (mPrimaryHWVsyncEnabled) {
+ eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, false);
+ mPrimaryDispSync.endResync();
+ mPrimaryHWVsyncEnabled = false;
+ }
+}
+
+void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) {
+ if (type == 0) {
+ bool needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
+
+ if (needsHwVsync) {
+ enableHardwareVsync();
+ } else {
+ disableHardwareVsync();
+ }
}
}
@@ -695,6 +821,7 @@ void SurfaceFlinger::onHotplugReceived(int type, bool connected) {
}
void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
+ ATRACE_CALL();
getHwComposer().eventControl(disp, event, enabled);
}
@@ -800,11 +927,27 @@ void SurfaceFlinger::postComposition()
layers[i]->onPostComposition();
}
+ const HWComposer& hwc = getHwComposer();
+ sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
+
+ if (presentFence->isValid()) {
+ if (mPrimaryDispSync.addPresentFence(presentFence)) {
+ enableHardwareVsync();
+ } else {
+ disableHardwareVsync();
+ }
+ }
+
+ if (runningWithoutSyncFramework) {
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
+ if (hw->isScreenAcquired()) {
+ enableHardwareVsync();
+ }
+ }
+
if (mAnimCompositionPending) {
mAnimCompositionPending = false;
- const HWComposer& hwc = getHwComposer();
- sp<Fence> presentFence = hwc.getDisplayFence(HWC_DISPLAY_PRIMARY);
if (presentFence->isValid()) {
mAnimFrameTracker.setActualPresentFence(presentFence);
} else {
@@ -2039,6 +2182,8 @@ void SurfaceFlinger::onScreenAcquired(const sp<const DisplayDevice>& hw) {
if (type == DisplayDevice::DISPLAY_PRIMARY) {
// FIXME: eventthread only knows about the main display right now
mEventThread->onScreenAcquired();
+
+ resyncToHardwareVsync();
}
}
mVisibleRegionsDirty = true;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3a79c06..2fd2586 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -49,6 +49,7 @@
#include "Barrier.h"
#include "DisplayDevice.h"
+#include "DispSync.h"
#include "FrameTracker.h"
#include "MessageQueue.h"
@@ -379,6 +380,12 @@ private:
* Display management
*/
+ /* ------------------------------------------------------------------------
+ * VSync
+ */
+ void enableHardwareVsync();
+ void disableHardwareVsync();
+ void resyncToHardwareVsync();
/* ------------------------------------------------------------------------
* Debugging & dumpsys
@@ -452,11 +459,16 @@ private:
// these are thread safe
mutable MessageQueue mEventQueue;
FrameTracker mAnimFrameTracker;
+ DispSync mPrimaryDispSync;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
Vector<Layer const *> mDestroyedLayers;
+ // protected by mHWVsyncLock
+ Mutex mHWVsyncLock;
+ bool mPrimaryHWVsyncEnabled;
+
/* ------------------------------------------------------------------------
* Feature prototyping
*/