diff options
-rw-r--r-- | services/surfaceflinger/Android.mk | 18 | ||||
-rw-r--r-- | services/surfaceflinger/DispSync.cpp | 482 | ||||
-rw-r--r-- | services/surfaceflinger/DispSync.h | 149 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayHardware/HWComposer.cpp | 2 | ||||
-rw-r--r-- | services/surfaceflinger/EventThread.cpp | 42 | ||||
-rw-r--r-- | services/surfaceflinger/EventThread.h | 26 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 173 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 12 |
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 */ |