OSDN Git Service

SurfaceFlinger: SW-based vsync events
authorJamie Gennis <jgennis@google.com>
Tue, 30 Jul 2013 22:10:32 +0000 (15:10 -0700)
committerJamie Gennis <jgennis@google.com>
Tue, 8 Oct 2013 00:59:53 +0000 (17:59 -0700)
This change adds the DispSync class, which models the hardware vsync event
times to allow vsync event callbacks to be done at an arbitrary phase offset
from the hardware vsync.  This can be used to reduce the minimum latency from
Choreographer wake-up to on-screen image presentation.

Bug: 10624956
Change-Id: I8c7a54ceacaa4d709726ed97b0dcae4093a7bdcf

services/surfaceflinger/Android.mk
services/surfaceflinger/DispSync.cpp [new file with mode: 0644]
services/surfaceflinger/DispSync.h [new file with mode: 0644]
services/surfaceflinger/DisplayHardware/HWComposer.cpp
services/surfaceflinger/EventThread.cpp
services/surfaceflinger/EventThread.h
services/surfaceflinger/SurfaceFlinger.cpp
services/surfaceflinger/SurfaceFlinger.h

index 7cc4ce1..bd89bd5 100644 (file)
@@ -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 (file)
index 0000000..7e67138
--- /dev/null
@@ -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 (file)
index 0000000..c4280aa
--- /dev/null
@@ -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
index 7132b2f..179d956 100644 (file)
@@ -285,7 +285,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);
index a61ad72..3528b62 100644 (file)
 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 {
index 5e88693..f6ab4a7 100644 (file)
@@ -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;
index e374548..3733ede 100644 (file)
@@ -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"
 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)
 {
     ALOGI("SurfaceFlinger is starting");
@@ -402,8 +435,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...");
 
@@ -499,9 +587,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;
 
@@ -656,17 +750,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();
+        }
     }
 }
 
@@ -694,6 +820,7 @@ void SurfaceFlinger::onHotplugReceived(int type, bool connected) {
 }
 
 void SurfaceFlinger::eventControl(int disp, int event, int enabled) {
+    ATRACE_CALL();
     getHwComposer().eventControl(disp, event, enabled);
 }
 
@@ -799,11 +926,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 {
@@ -2034,6 +2177,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;
index 0e9955c..f1c19c2 100644 (file)
@@ -48,6 +48,7 @@
 
 #include "Barrier.h"
 #include "DisplayDevice.h"
+#include "DispSync.h"
 #include "FrameTracker.h"
 #include "MessageQueue.h"
 
@@ -378,6 +379,12 @@ private:
      * Display management
      */
 
+    /* ------------------------------------------------------------------------
+     * VSync
+     */
+     void enableHardwareVsync();
+     void disableHardwareVsync();
+     void resyncToHardwareVsync();
 
     /* ------------------------------------------------------------------------
      * Debugging & dumpsys
@@ -451,11 +458,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
      */