diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp index a00ccdbd6..fd4682225 100644 --- a/services/surfaceflinger/DispSync.cpp +++ b/services/surfaceflinger/DispSync.cpp @@ -508,4 +508,9 @@ void DispSync::resetErrorLocked() { } } +nsecs_t DispSync::computeNextRefresh(int periodOffset) const { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + return (((now - mPhase) / mPeriod) + periodOffset + 1) * mPeriod + mPhase; +} + } // namespace android diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h index c2ae135b0..5826a785c 100644 --- a/services/surfaceflinger/DispSync.h +++ b/services/surfaceflinger/DispSync.h @@ -83,7 +83,7 @@ public: bool addResyncSample(nsecs_t timestamp); void endResync(); - // The setPreiod method sets the vsync event model's period to a specific + // The setPeriod method sets the vsync event model's period to a specific // value. This should be used to prime the model when a display is first // turned on. It should NOT be used after that. void setPeriod(nsecs_t period); @@ -102,6 +102,12 @@ public: // DispSync object. status_t removeEventListener(const sp& callback); + // computeNextRefresh computes when the next refresh is expected to begin. + // The periodOffset value can be used to move forward or backward; an + // offset of zero is the next refresh, -1 is the previous refresh, 1 is + // the refresh after next. etc. + nsecs_t computeNextRefresh(int periodOffset) const; + private: void updateModelLocked(); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index c4ea8cca2..2d63bde9f 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -821,7 +821,7 @@ status_t HWComposer::setOutputBuffer(int32_t id, const sp& acquireFence, return NO_ERROR; } -sp HWComposer::getLastRetireFence(int32_t id) { +sp HWComposer::getLastRetireFence(int32_t id) const { if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) return Fence::NO_FENCE; return mDisplayData[id].lastRetireFence; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 9218bf609..e93bb7e3b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -142,7 +142,7 @@ public: // signal when the h/w composer is completely finished with the frame. // For physical displays, it is no longer being displayed. For virtual // displays, writes to the output buffer are complete. - sp getLastRetireFence(int32_t id); + sp getLastRetireFence(int32_t id) const; /* * Interface to hardware composer's layers functionality. diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 1b8620498..63bc257f5 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1095,7 +1095,8 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) Reject r(mDrawingState, getCurrentState(), recomputeVisibleRegions); - status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r); + status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r, + mFlinger->mPrimaryDispSync); if (updateResult == BufferQueue::PRESENT_LATER) { // Producer doesn't want buffer to be displayed yet. Signal a // layer update so we check again at the next opportunity. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 0142992a8..7bd8d3818 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2371,6 +2371,15 @@ void SurfaceFlinger::dumpAllLocked(const Vector& args, size_t& index, result.append(SyncFeatures::getInstance().toString()); result.append("\n"); + colorizer.bold(result); + result.append("DispSync configuration: "); + colorizer.reset(result); + result.appendFormat("app phase %"PRId64" ns, sf phase %"PRId64" ns, " + "present offset %d ns (refresh %"PRId64" ns)", + vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, PRESENT_TIME_OFFSET_FROM_VSYNC_NS, + mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY)); + result.append("\n"); + /* * Dump the visible layer list */ diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp index a412543ef..7de6ac428 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -29,7 +29,8 @@ namespace android { // --------------------------------------------------------------------------- -status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) +status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter, + const DispSync& dispSync) { ATRACE_CALL(); ALOGV("updateTexImage"); @@ -51,7 +52,7 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. - err = acquireBufferLocked(&item, computeExpectedPresent()); + err = acquireBufferLocked(&item, computeExpectedPresent(dispSync)); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { err = NO_ERROR; @@ -128,35 +129,41 @@ sp SurfaceFlingerConsumer::getSidebandStream() const { // will be slightly later then the actual-present timing. If we get a // desired-present time that is unintentionally a hair after the next // vsync, we'll hold the frame when we really want to display it. We -// want to use an expected-presentation time that is slightly late to -// avoid this sort of edge case. -nsecs_t SurfaceFlingerConsumer::computeExpectedPresent() +// need to take the offset between actual-present and reported-vsync +// into account. +// +// If the system is configured without a DispSync phase offset for the app, +// we also want to throw in a bit of padding to avoid edge cases where we +// just barely miss. We want to do it here, not in every app. A major +// source of trouble is the app's use of the display's ideal refresh time +// (via Display.getRefreshRate()), which could be off of the actual refresh +// by a few percent, with the error multiplied by the number of frames +// between now and when the buffer should be displayed. +// +// If the refresh reported to the app has a phase offset, we shouldn't need +// to tweak anything here. +nsecs_t SurfaceFlingerConsumer::computeExpectedPresent(const DispSync& dispSync) { - // Don't yet have an easy way to get actual buffer flip time for - // the specific display, so use the current time. This is typically - // 1.3ms past the vsync event time. - const nsecs_t prevVsync = systemTime(CLOCK_MONOTONIC); - - // Given a SurfaceFlinger reference, and information about what display - // we're destined for, we could query the HWC for the refresh rate. This - // could change over time, e.g. we could switch to 24fps for a movie. - // For now, assume 60fps. - //const nsecs_t vsyncPeriod = - // getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); - const nsecs_t vsyncPeriod = 16700000; - // The HWC doesn't currently have a way to report additional latency. - // Assume that whatever we submit now will appear on the next flip, - // i.e. 1 frame of latency w.r.t. the previous flip. - const uint32_t hwcLatency = 1; + // Assume that whatever we submit now will appear right after the flip. + // For a smart panel this might be 1. This is expressed in frames, + // rather than time, because we expect to have a constant frame delay + // regardless of the refresh rate. + const uint32_t hwcLatency = 0; - // A little extra padding to compensate for slack between actual vsync - // time and vsync event receipt. Currently not needed since we're - // using "now" instead of a vsync time. - const nsecs_t extraPadding = 0; + // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC). + const nsecs_t nextRefresh = dispSync.computeNextRefresh(hwcLatency); - // Total it up. - return prevVsync + hwcLatency * vsyncPeriod + extraPadding; + // The DispSync time is already adjusted for the difference between + // vsync and reported-vsync (PRESENT_TIME_OFFSET_FROM_VSYNC_NS), so + // we don't need to factor that in here. Pad a little to avoid + // weird effects if apps might be requesting times right on the edge. + nsecs_t extraPadding = 0; + if (VSYNC_EVENT_PHASE_OFFSET_NS == 0) { + extraPadding = 1000000; // 1ms (6% of 60Hz) + } + + return nextRefresh + extraPadding; } void SurfaceFlingerConsumer::setContentsChangedListener( diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h index 0f1bf3583..ed307c214 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.h +++ b/services/surfaceflinger/SurfaceFlingerConsumer.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SURFACEFLINGERCONSUMER_H #define ANDROID_SURFACEFLINGERCONSUMER_H +#include "DispSync.h" #include namespace android { @@ -33,7 +34,8 @@ public: SurfaceFlingerConsumer(const sp& consumer, uint32_t tex) - : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false) + : GLConsumer(consumer, tex, GLConsumer::TEXTURE_EXTERNAL, false), + mTransformToDisplayInverse(false) {} class BufferRejecter { @@ -51,7 +53,7 @@ public: // reject the newly acquired buffer. Unlike the GLConsumer version, // this does not guarantee that the buffer has been bound to the GL // texture. - status_t updateTexImage(BufferRejecter* rejecter); + status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync); // See GLConsumer::bindTextureImageLocked(). status_t bindTextureImage(); @@ -66,7 +68,7 @@ public: sp getSidebandStream() const; private: - nsecs_t computeExpectedPresent(); + nsecs_t computeExpectedPresent(const DispSync& dispSync); virtual void onSidebandStreamChanged();