diff options
| -rw-r--r-- | include/gui/FrameTimestamps.h | 27 | ||||
| -rw-r--r-- | include/gui/Surface.h | 11 | ||||
| -rw-r--r-- | libs/gui/FrameTimestamps.cpp | 48 | ||||
| -rw-r--r-- | libs/gui/Surface.cpp | 81 | ||||
| -rw-r--r-- | libs/gui/tests/Surface_test.cpp | 307 | ||||
| -rw-r--r-- | opengl/include/EGL/eglext.h | 31 | ||||
| -rw-r--r-- | opengl/libs/EGL/eglApi.cpp | 106 | ||||
| -rw-r--r-- | opengl/specs/EGL_ANDROID_get_frame_timestamps.txt | 72 | ||||
| -rw-r--r-- | opengl/specs/README | 25 | ||||
| -rw-r--r-- | services/surfaceflinger/Layer.cpp | 12 | ||||
| -rw-r--r-- | services/surfaceflinger/Layer.h | 6 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 88 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 29 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger_hwc1.cpp | 75 |
14 files changed, 809 insertions, 109 deletions
diff --git a/include/gui/FrameTimestamps.h b/include/gui/FrameTimestamps.h index 46ca2c2ec0..9e1ae9409e 100644 --- a/include/gui/FrameTimestamps.h +++ b/include/gui/FrameTimestamps.h @@ -95,6 +95,11 @@ struct FrameEvents { std::shared_ptr<FenceTime> releaseFence{FenceTime::NO_FENCE}; }; +struct CompositorTiming { + nsecs_t deadline{0}; + nsecs_t interval{16666667}; + nsecs_t presentLatency{0}; +}; // A short history of frames that are synchronized between the consumer and // producer via deltas. @@ -111,6 +116,8 @@ public: protected: std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames; + + CompositorTiming mCompositorTiming; }; @@ -119,6 +126,16 @@ class ProducerFrameEventHistory : public FrameEventHistory { public: ~ProducerFrameEventHistory() override; + // Public for testing. + static nsecs_t snapToNextTick( + nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval); + + nsecs_t getNextCompositeDeadline(const nsecs_t now) const; + nsecs_t getCompositeInterval() const { return mCompositorTiming.interval; } + nsecs_t getCompositeToPresentLatency() const { + return mCompositorTiming.presentLatency; + } + // virtual for testing. virtual void updateAcquireFence( uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire); @@ -189,12 +206,15 @@ class ConsumerFrameEventHistory : public FrameEventHistory { public: ~ConsumerFrameEventHistory() override; + void initializeCompositorTiming(const CompositorTiming& compositorTiming); + void addQueue(const NewFrameEventsEntry& newEntry); void addLatch(uint64_t frameNumber, nsecs_t latchTime); void addPreComposition(uint64_t frameNumber, nsecs_t refreshStartTime); void addPostComposition(uint64_t frameNumber, const std::shared_ptr<FenceTime>& gpuCompositionDone, - const std::shared_ptr<FenceTime>& displayPresent); + const std::shared_ptr<FenceTime>& displayPresent, + const CompositorTiming& compositorTiming); void addRetire(uint64_t frameNumber, const std::shared_ptr<FenceTime>& displayRetire); void addRelease(uint64_t frameNumber, nsecs_t dequeueReadyTime, @@ -244,7 +264,7 @@ public: size_t& count); private: - static size_t minFlattenedSize(); + static constexpr size_t minFlattenedSize(); size_t mIndex{0}; uint64_t mFrameNumber{0}; @@ -306,9 +326,10 @@ public: size_t& count); private: - static size_t minFlattenedSize(); + static constexpr size_t minFlattenedSize(); std::vector<FrameEventsDelta> mDeltas; + CompositorTiming mCompositorTiming; }; diff --git a/include/gui/Surface.h b/include/gui/Surface.h index 4baa6aa9ec..d05d9307bc 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -137,12 +137,18 @@ public: status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]); + status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration); + /* Enables or disables frame timestamp tracking. It is disabled by default * to avoid overhead during queue and dequeue for applications that don't * need the feature. If disabled, calls to getFrameTimestamps will fail. */ void enableFrameTimestamps(bool enable); + status_t getCompositorTiming( + nsecs_t* compositeDeadline, nsecs_t* compositeInterval, + nsecs_t* compositeToPresentLatency); + // See IGraphicBufferProducer::getFrameTimestamps status_t getFrameTimestamps(uint64_t frameNumber, nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, @@ -150,7 +156,6 @@ public: nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime, nsecs_t* outDisplayPresentTime, nsecs_t* outDisplayRetireTime, nsecs_t* outDequeueReadyTime, nsecs_t* outReleaseTime); - status_t getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration); status_t getUniqueId(uint64_t* outId) const; @@ -159,6 +164,7 @@ protected: // Virtual for testing. virtual sp<ISurfaceComposer> composerService() const; + virtual nsecs_t now() const; private: // can't be copied @@ -206,10 +212,11 @@ private: int dispatchSetSurfaceDamage(va_list args); int dispatchSetSharedBufferMode(va_list args); int dispatchSetAutoRefresh(va_list args); + int dispatchGetDisplayRefreshCycleDuration(va_list args); int dispatchGetNextFrameId(va_list args); int dispatchEnableFrameTimestamps(va_list args); + int dispatchGetCompositorTiming(va_list args); int dispatchGetFrameTimestamps(va_list args); - int dispatchGetDisplayRefreshCycleDuration(va_list args); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index 73537bfc27..a6fa38aae5 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -235,6 +235,23 @@ void FrameEventHistory::dump(String8& outString) const { ProducerFrameEventHistory::~ProducerFrameEventHistory() = default; +nsecs_t ProducerFrameEventHistory::snapToNextTick( + nsecs_t timestamp, nsecs_t tickPhase, nsecs_t tickInterval) { + nsecs_t tickOffset = (tickPhase - timestamp) % tickInterval; + // Integer modulo rounds towards 0 and not -inf before taking the remainder, + // so adjust the offset if it is negative. + if (tickOffset < 0) { + tickOffset += tickInterval; + } + return timestamp + tickOffset; +} + +nsecs_t ProducerFrameEventHistory::getNextCompositeDeadline( + const nsecs_t now) const{ + return snapToNextTick( + now, mCompositorTiming.deadline, mCompositorTiming.interval); +} + void ProducerFrameEventHistory::updateAcquireFence( uint64_t frameNumber, std::shared_ptr<FenceTime>&& acquire) { FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset); @@ -256,6 +273,8 @@ void ProducerFrameEventHistory::updateAcquireFence( void ProducerFrameEventHistory::applyDelta( const FrameEventHistoryDelta& delta) { + mCompositorTiming = delta.mCompositorTiming; + for (auto& d : delta.mDeltas) { // Avoid out-of-bounds access. if (d.mIndex >= mFrames.size()) { @@ -346,6 +365,11 @@ std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; +void ConsumerFrameEventHistory::initializeCompositorTiming( + const CompositorTiming& compositorTiming) { + mCompositorTiming = compositorTiming; +} + void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) { // Overwrite all fields of the frame with default values unless set here. FrameEvents newTimestamps; @@ -393,7 +417,10 @@ void ConsumerFrameEventHistory::addPreComposition( void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber, const std::shared_ptr<FenceTime>& gpuCompositionDone, - const std::shared_ptr<FenceTime>& displayPresent) { + const std::shared_ptr<FenceTime>& displayPresent, + const CompositorTiming& compositorTiming) { + mCompositorTiming = compositorTiming; + FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset); if (frame == nullptr) { ALOGE_IF(mProducerWantsEvents, @@ -450,6 +477,8 @@ void ConsumerFrameEventHistory::getFrameDelta( void ConsumerFrameEventHistory::getAndResetDelta( FrameEventHistoryDelta* delta) { + delta->mCompositorTiming = mCompositorTiming; + // Write these in order of frame number so that it is easy to // add them to a FenceTimeline in the proper order producer side. delta->mDeltas.reserve(mFramesDirty.size()); @@ -499,9 +528,8 @@ FrameEventsDelta::FrameEventsDelta( } } -size_t FrameEventsDelta::minFlattenedSize() { - constexpr size_t min = - sizeof(FrameEventsDelta::mFrameNumber) + +constexpr size_t FrameEventsDelta::minFlattenedSize() { + return sizeof(FrameEventsDelta::mFrameNumber) + sizeof(uint8_t) + // mIndex sizeof(uint8_t) + // mAddPostCompositeCalled sizeof(uint8_t) + // mAddRetireCalled @@ -512,7 +540,6 @@ size_t FrameEventsDelta::minFlattenedSize() { sizeof(FrameEventsDelta::mFirstRefreshStartTime) + sizeof(FrameEventsDelta::mLastRefreshStartTime) + sizeof(FrameEventsDelta::mDequeueReadyTime); - return min; } // Flattenable implementation @@ -618,6 +645,8 @@ status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( FrameEventHistoryDelta&& src) { + mCompositorTiming = src.mCompositorTiming; + if (CC_UNLIKELY(!mDeltas.empty())) { ALOGE("FrameEventHistoryDelta: Clobbering history."); } @@ -626,8 +655,9 @@ FrameEventHistoryDelta& FrameEventHistoryDelta::operator=( return *this; } -size_t FrameEventHistoryDelta::minFlattenedSize() { - return sizeof(uint32_t); +constexpr size_t FrameEventHistoryDelta::minFlattenedSize() { + return sizeof(uint32_t) + // mDeltas.size() + sizeof(mCompositorTiming); } size_t FrameEventHistoryDelta::getFlattenedSize() const { @@ -654,6 +684,8 @@ status_t FrameEventHistoryDelta::flatten( return NO_MEMORY; } + FlattenableUtils::write(buffer, size, mCompositorTiming); + FlattenableUtils::write( buffer, size, static_cast<uint32_t>(mDeltas.size())); for (auto& d : mDeltas) { @@ -671,6 +703,8 @@ status_t FrameEventHistoryDelta::unflatten( return NO_MEMORY; } + FlattenableUtils::read(buffer, size, mCompositorTiming); + uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) { diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 2e3a7dedfb..b250f5d4b7 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -102,6 +102,10 @@ sp<ISurfaceComposer> Surface::composerService() const { return ComposerService::getComposerService(); } +nsecs_t Surface::now() const { + return systemTime(); +} + sp<IGraphicBufferProducer> Surface::getIGraphicBufferProducer() const { return mGraphicBufferProducer; } @@ -144,11 +148,51 @@ status_t Surface::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, outTransformMatrix); } +status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { + ATRACE_CALL(); + + DisplayStatInfo stats; + status_t err = composerService()->getDisplayStats(NULL, &stats); + + *outRefreshDuration = stats.vsyncPeriod; + + return NO_ERROR; +} + void Surface::enableFrameTimestamps(bool enable) { Mutex::Autolock lock(mMutex); + // If going from disabled to enabled, get the initial values for + // compositor and display timing. + if (!mEnableFrameTimestamps && enable) { + FrameEventHistoryDelta delta; + mGraphicBufferProducer->getFrameTimestamps(&delta); + mFrameEventHistory->applyDelta(delta); + } mEnableFrameTimestamps = enable; } +status_t Surface::getCompositorTiming( + nsecs_t* compositeDeadline, nsecs_t* compositeInterval, + nsecs_t* compositeToPresentLatency) { + Mutex::Autolock lock(mMutex); + if (!mEnableFrameTimestamps) { + return INVALID_OPERATION; + } + + if (compositeDeadline != nullptr) { + *compositeDeadline = + mFrameEventHistory->getNextCompositeDeadline(now()); + } + if (compositeInterval != nullptr) { + *compositeInterval = mFrameEventHistory->getCompositeInterval(); + } + if (compositeToPresentLatency != nullptr) { + *compositeToPresentLatency = + mFrameEventHistory->getCompositeToPresentLatency(); + } + return NO_ERROR; +} + static bool checkConsumerForUpdates( const FrameEvents* e, const uint64_t lastFrameNumber, const nsecs_t* outLatchTime, @@ -262,16 +306,6 @@ status_t Surface::getFrameTimestamps(uint64_t frameNumber, return NO_ERROR; } -status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { - ATRACE_CALL(); - - DisplayStatInfo stats; - status_t err = composerService()->getDisplayStats(NULL, &stats); - - *outRefreshDuration = stats.vsyncPeriod; - - return NO_ERROR; -} int Surface::hook_setSwapInterval(ANativeWindow* window, int interval) { Surface* c = getSelf(window); @@ -833,18 +867,21 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_AUTO_REFRESH: res = dispatchSetAutoRefresh(args); break; + case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION: + res = dispatchGetDisplayRefreshCycleDuration(args); + break; case NATIVE_WINDOW_GET_NEXT_FRAME_ID: res = dispatchGetNextFrameId(args); break; case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS: res = dispatchEnableFrameTimestamps(args); break; + case NATIVE_WINDOW_GET_COMPOSITOR_TIMING: + res = dispatchGetCompositorTiming(args); + break; case NATIVE_WINDOW_GET_FRAME_TIMESTAMPS: res = dispatchGetFrameTimestamps(args); break; - case NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION: - res = dispatchGetDisplayRefreshCycleDuration(args); - break; default: res = NAME_NOT_FOUND; break; @@ -965,6 +1002,11 @@ int Surface::dispatchSetAutoRefresh(va_list args) { return setAutoRefresh(autoRefresh); } +int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) { + nsecs_t* outRefreshDuration = va_arg(args, int64_t*); + return getDisplayRefreshCycleDuration(outRefreshDuration); +} + int Surface::dispatchGetNextFrameId(va_list args) { uint64_t* nextFrameId = va_arg(args, uint64_t*); *nextFrameId = getNextFrameNumber(); @@ -977,6 +1019,14 @@ int Surface::dispatchEnableFrameTimestamps(va_list args) { return NO_ERROR; } +int Surface::dispatchGetCompositorTiming(va_list args) { + nsecs_t* compositeDeadline = va_arg(args, int64_t*); + nsecs_t* compositeInterval = va_arg(args, int64_t*); + nsecs_t* compositeToPresentLatency = va_arg(args, int64_t*); + return getCompositorTiming(compositeDeadline, compositeInterval, + compositeToPresentLatency); +} + int Surface::dispatchGetFrameTimestamps(va_list args) { uint64_t frameId = va_arg(args, uint64_t); nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*); @@ -996,11 +1046,6 @@ int Surface::dispatchGetFrameTimestamps(va_list args) { outDisplayRetireTime, outDequeueReadyTime, outReleaseTime); } -int Surface::dispatchGetDisplayRefreshCycleDuration(va_list args) { - nsecs_t* outRefreshDuration = va_arg(args, int64_t*); - return getDisplayRefreshCycleDuration(outRefreshDuration); -} - int Surface::connect(int api) { static sp<IProducerListener> listener = new DummyProducerListener(); return connect(api, listener); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 3f56665a61..52980270f2 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -479,8 +479,17 @@ public: return mFakeSurfaceComposer; } + nsecs_t now() const override { + return mNow; + } + + void setNow(nsecs_t now) { + mNow = now; + } + public: sp<FakeSurfaceComposer> mFakeSurfaceComposer; + nsecs_t mNow = 0; // mFrameEventHistory owns the instance of FakeProducerFrameEventHistory, // but this raw pointer gives access to test functionality. @@ -500,10 +509,12 @@ protected: struct RefreshEvents { RefreshEvents(FenceToFenceTimeMap& fenceMap, nsecs_t refreshStart) - : mFenceMap(fenceMap), - kStartTime(refreshStart + 1), - kGpuCompositionDoneTime(refreshStart + 2), - kPresentTime(refreshStart + 3) {} + : mFenceMap(fenceMap), + kCompositorTiming( + {refreshStart, refreshStart + 1, refreshStart + 2 }), + kStartTime(refreshStart + 3), + kGpuCompositionDoneTime(refreshStart + 4), + kPresentTime(refreshStart + 5) {} void signalPostCompositeFences() { mFenceMap.signalAllForTest( @@ -516,6 +527,8 @@ protected: FenceAndFenceTime mGpuCompositionDone { mFenceMap }; FenceAndFenceTime mPresent { mFenceMap }; + const CompositorTiming kCompositorTiming; + const nsecs_t kStartTime; const nsecs_t kGpuCompositionDoneTime; const nsecs_t kPresentTime; @@ -592,6 +605,12 @@ protected: native_window_set_buffer_count(mWindow.get(), 4); } + void disableFrameTimestamps() { + mFakeConsumer->mGetFrameTimestampsEnabled = false; + native_window_enable_frame_timestamps(mWindow.get(), 0); + mFrameTimestampsEnabled = false; + } + void enableFrameTimestamps() { mFakeConsumer->mGetFrameTimestampsEnabled = true; native_window_enable_frame_timestamps(mWindow.get(), 1); @@ -681,7 +700,8 @@ protected: oldFrame->mRefreshes[2].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nOldFrame, gpuDoneFenceTime, - oldFrame->mRefreshes[2].mPresent.mFenceTime); + oldFrame->mRefreshes[2].mPresent.mFenceTime, + oldFrame->mRefreshes[2].kCompositorTiming); } // Latch the new frame. @@ -698,7 +718,8 @@ protected: std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime)); } mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, - newFrame->mRefreshes[0].mPresent.mFenceTime); + newFrame->mRefreshes[0].mPresent.mFenceTime, + newFrame->mRefreshes[0].kCompositorTiming); // Retire the previous buffer just after compositing the new buffer. if (oldFrame != nullptr) { @@ -710,7 +731,8 @@ protected: newFrame->mRefreshes[1].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; mCfeh->addPostComposition(nNewFrame, gpuDoneFenceTime, - newFrame->mRefreshes[1].mPresent.mFenceTime); + newFrame->mRefreshes[1].mPresent.mFenceTime, + newFrame->mRefreshes[1].kCompositorTiming); } void QueryPresentRetireSupported( @@ -740,7 +762,8 @@ protected: int64_t outDequeueReadyTime = -1; int64_t outReleaseTime = -1; - FrameEvents mFrames[2] { { mFenceMap, 1000 }, { mFenceMap, 2000 } }; + FrameEvents mFrames[3] { + { mFenceMap, 1000 }, { mFenceMap, 2000 }, { mFenceMap, 3000 } }; }; @@ -773,25 +796,55 @@ TEST_F(GetFrameTimestampsTest, DefaultDisabled) { int result = getAllFrameTimestamps(fId); EXPECT_EQ(INVALID_OPERATION, result); EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + + // Verify compositor timing query fails. + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(INVALID_OPERATION, result); } // This test verifies that the frame timestamps are retrieved if explicitly // enabled via native_window_enable_frame_timestamps. TEST_F(GetFrameTimestampsTest, EnabledSimple) { + CompositorTiming initialCompositorTiming { + 1000000000, // 1s deadline + 16666667, // 16ms interval + 50000000, // 50ms present latency + }; + mCfeh->initializeCompositorTiming(initialCompositorTiming); + enableFrameTimestamps(); + // Verify the compositor timing query gets the initial compositor values + // after timststamps are enabled; even before the first frame is queued + // or dequeued. + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + mSurface->setNow(initialCompositorTiming.deadline - 1); + int result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); + EXPECT_EQ(initialCompositorTiming.presentLatency, + compositeToPresentLatency); + int fence; ANativeWindowBuffer* buffer; EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); - EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount); const uint64_t fId1 = getNextFrameId(); // Verify getFrameTimestamps is piggybacked on dequeue. ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence)); EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount); - EXPECT_EQ(1, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount); NewFrameEventsEntry f1; f1.frameNumber = 1; @@ -808,13 +861,13 @@ TEST_F(GetFrameTimestampsTest, EnabledSimple) { ASSERT_EQ(NO_ERROR, mWindow->queueBuffer(mWindow.get(), buffer, fence)); EXPECT_EQ(1, mFakeConsumer->mAddFrameTimestampsCount); EXPECT_EQ(1u, mFakeConsumer->mLastAddedFrameNumber); - EXPECT_EQ(2, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount); // Verify queries for timestamps that the producer doesn't know about // triggers a call to see if the consumer has any new timestamps. - int result = getAllFrameTimestamps(fId1); + result = getAllFrameTimestamps(fId1); EXPECT_EQ(NO_ERROR, result); - EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount); + EXPECT_EQ(4, mFakeConsumer->mGetFrameTimestampsCount); } void GetFrameTimestampsTest::QueryPresentRetireSupported( @@ -842,6 +895,234 @@ TEST_F(GetFrameTimestampsTest, QueryRetireSupported) { QueryPresentRetireSupported(false, true); } +TEST_F(GetFrameTimestampsTest, SnapToNextTickBasic) { + nsecs_t phase = 4000; + nsecs_t interval = 1000; + + // Timestamp in previous interval. + nsecs_t timestamp = 3500; + EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp in next interval. + timestamp = 4500; + EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp multiple intervals before. + timestamp = 2500; + EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp multiple intervals after. + timestamp = 6500; + EXPECT_EQ(7000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp on previous interval. + timestamp = 3000; + EXPECT_EQ(3000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp on next interval. + timestamp = 5000; + EXPECT_EQ(5000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); + + // Timestamp equal to phase. + timestamp = 4000; + EXPECT_EQ(4000, ProducerFrameEventHistory::snapToNextTick( + timestamp, phase, interval)); +} + +// int(big_timestamp / interval) < 0, which can cause a crash or invalid result +// if the number of intervals elapsed is internally stored in an int. +TEST_F(GetFrameTimestampsTest, SnapToNextTickOverflow) { + nsecs_t phase = 0; + nsecs_t interval = 4000; + nsecs_t big_timestamp = 8635916564000; + int32_t intervals = big_timestamp / interval; + + EXPECT_LT(intervals, 0); + EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( + big_timestamp, phase, interval)); + EXPECT_EQ(8635916564000, ProducerFrameEventHistory::snapToNextTick( + big_timestamp, big_timestamp, interval)); +} + +// This verifies the compositor timing is updated by refresh events +// and piggy backed on a queue, dequeue, and enabling of timestamps.. +TEST_F(GetFrameTimestampsTest, CompositorTimingUpdatesBasic) { + CompositorTiming initialCompositorTiming { + 1000000000, // 1s deadline + 16666667, // 16ms interval + 50000000, // 50ms present latency + }; + mCfeh->initializeCompositorTiming(initialCompositorTiming); + + enableFrameTimestamps(); + + // We get the initial values before any frames are submitted. + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + mSurface->setNow(initialCompositorTiming.deadline - 1); + int result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); + EXPECT_EQ(initialCompositorTiming.presentLatency, + compositeToPresentLatency); + + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + addFrameEvents(true, NO_FRAME_INDEX, 0); + + // Still get the initial values because the frame events for frame 0 + // didn't get a chance to piggyback on a queue or dequeue yet. + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + EXPECT_EQ(initialCompositorTiming.interval, compositeInterval); + EXPECT_EQ(initialCompositorTiming.presentLatency, + compositeToPresentLatency); + + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + addFrameEvents(true, 0, 1); + + // Now expect the composite values associated with frame 1. + mSurface->setNow(mFrames[0].mRefreshes[1].kCompositorTiming.deadline); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.deadline, + compositeDeadline); + EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.interval, + compositeInterval); + EXPECT_EQ(mFrames[0].mRefreshes[1].kCompositorTiming.presentLatency, + compositeToPresentLatency); + + dequeueAndQueue(2); + addFrameEvents(true, 1, 2); + + // Now expect the composite values associated with frame 2. + mSurface->setNow(mFrames[1].mRefreshes[1].kCompositorTiming.deadline); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.deadline, + compositeDeadline); + EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.interval, + compositeInterval); + EXPECT_EQ(mFrames[1].mRefreshes[1].kCompositorTiming.presentLatency, + compositeToPresentLatency); + + // Re-enabling frame timestamps should get the latest values. + disableFrameTimestamps(); + enableFrameTimestamps(); + + // Now expect the composite values associated with frame 3. + mSurface->setNow(mFrames[2].mRefreshes[1].kCompositorTiming.deadline); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.deadline, + compositeDeadline); + EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.interval, + compositeInterval); + EXPECT_EQ(mFrames[2].mRefreshes[1].kCompositorTiming.presentLatency, + compositeToPresentLatency); +} + +// This verifies the compositor deadline properly snaps to the the next +// deadline based on the current time. +TEST_F(GetFrameTimestampsTest, CompositorTimingDeadlineSnaps) { + CompositorTiming initialCompositorTiming { + 1000000000, // 1s deadline + 16666667, // 16ms interval + 50000000, // 50ms present latency + }; + mCfeh->initializeCompositorTiming(initialCompositorTiming); + + enableFrameTimestamps(); + + nsecs_t compositeDeadline = 0; + nsecs_t compositeInterval = 0; + nsecs_t compositeToPresentLatency = 0; + + // A "now" just before the deadline snaps to the deadline. + mSurface->setNow(initialCompositorTiming.deadline - 1); + int result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + EXPECT_EQ(initialCompositorTiming.deadline, compositeDeadline); + nsecs_t expectedDeadline = initialCompositorTiming.deadline; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + const uint64_t fId1 = getNextFrameId(); + dequeueAndQueue(0); + addFrameEvents(true, NO_FRAME_INDEX, 0); + + // A "now" just after the deadline snaps properly. + mSurface->setNow(initialCompositorTiming.deadline + 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + initialCompositorTiming.deadline +initialCompositorTiming.interval; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + const uint64_t fId2 = getNextFrameId(); + dequeueAndQueue(1); + addFrameEvents(true, 0, 1); + + // A "now" just after the next interval snaps properly. + mSurface->setNow( + mFrames[0].mRefreshes[1].kCompositorTiming.deadline + + mFrames[0].mRefreshes[1].kCompositorTiming.interval + 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + mFrames[0].mRefreshes[1].kCompositorTiming.deadline + + mFrames[0].mRefreshes[1].kCompositorTiming.interval * 2; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + dequeueAndQueue(2); + addFrameEvents(true, 1, 2); + + // A "now" over 1 interval before the deadline snaps properly. + mSurface->setNow( + mFrames[1].mRefreshes[1].kCompositorTiming.deadline - + mFrames[1].mRefreshes[1].kCompositorTiming.interval - 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + mFrames[1].mRefreshes[1].kCompositorTiming.deadline - + mFrames[1].mRefreshes[1].kCompositorTiming.interval; + EXPECT_EQ(expectedDeadline, compositeDeadline); + + // Re-enabling frame timestamps should get the latest values. + disableFrameTimestamps(); + enableFrameTimestamps(); + + // A "now" over 2 intervals before the deadline snaps properly. + mSurface->setNow( + mFrames[2].mRefreshes[1].kCompositorTiming.deadline - + mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2 - 1); + result = native_window_get_compositor_timing(mWindow.get(), + &compositeDeadline, &compositeInterval, &compositeToPresentLatency); + EXPECT_EQ(NO_ERROR, result); + expectedDeadline = + mFrames[2].mRefreshes[1].kCompositorTiming.deadline - + mFrames[2].mRefreshes[1].kCompositorTiming.interval * 2; + EXPECT_EQ(expectedDeadline, compositeDeadline); +} + // This verifies the timestamps recorded in the consumer's // FrameTimestampsHistory are properly retrieved by the producer for the // correct frames. diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index 6485ae5c1d..6572cab466 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -632,24 +632,31 @@ typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLCREATENATIVECLIENTBUFFERANDROID) (co #ifndef EGL_ANDROID_get_frame_timestamps #define EGL_ANDROID_get_frame_timestamps 1 #define EGL_TIMESTAMPS_ANDROID 0x314D -#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E -#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F -#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150 -#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151 -#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152 -#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153 -#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154 -#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155 -#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3156 -#define EGL_READS_DONE_TIME_ANDROID 0x3157 +#define EGL_COMPOSITE_DEADLINE_ANDROID 0x314E +#define EGL_COMPOSITE_INTERVAL_ANDROID 0x314F +#define EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150 +#define EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151 +#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152 +#define EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153 +#define EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154 +#define EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155 +#define EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3156 +#define EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157 +#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158 +#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3159 +#define EGL_READS_DONE_TIME_ANDROID 0x315A #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); +EGLAPI EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values); +EGLAPI EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint name); EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); -EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +EGLAPI EGLBoolean eglGetFrameTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp); #else typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETNEXTFRAMEIDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETCOMPOSITORTIMINGSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint name); typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); -typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp); #endif #endif diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index c2fc6bd394..42d01b2103 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -218,10 +218,14 @@ static const extention_map_t sExtensionMap[] = { // EGL_ANDROID_get_frame_timestamps { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, + { "eglGetCompositorTimingANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, + { "eglGetCompositorTimingSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, - { "eglQueryTimestampSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID }, + { "eglGetFrameTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, }; /* @@ -2078,6 +2082,97 @@ EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, return EGL_TRUE; } +EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, EGLSurface surface, + EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->win.get()) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + nsecs_t* compositeDeadline = nullptr; + nsecs_t* compositeInterval = nullptr; + nsecs_t* compositeToPresentLatency = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (names[i]) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + compositeDeadline = &values[i]; + break; + case EGL_COMPOSITE_INTERVAL_ANDROID: + compositeInterval = &values[i]; + break; + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + compositeToPresentLatency = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, EGL_FALSE); + } + } + + status_t ret = native_window_get_compositor_timing(s->win.get(), + compositeDeadline, compositeInterval, compositeToPresentLatency); + + switch (ret) { + case NO_ERROR: + return EGL_TRUE; + case INVALID_OPERATION: + return setError(EGL_BAD_SURFACE, EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetCompositorTiming: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, EGL_FALSE); + } +} + +EGLBoolean eglGetCompositorTimingSupportedANDROID( + EGLDisplay dpy, EGLSurface surface, EGLint name) +{ + clearError(); + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->win.get(); + if (!window) { + return setError(EGL_BAD_SURFACE, EGL_FALSE); + } + + switch (name) { +#if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + return EGL_TRUE; +#endif + default: + return EGL_FALSE; + } +} + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values) @@ -2170,8 +2265,8 @@ EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, } } -EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, - EGLint timestamp) +EGLBoolean eglGetFrameTimestampSupportedANDROID( + EGLDisplay dpy, EGLSurface surface, EGLint timestamp) { clearError(); @@ -2194,6 +2289,9 @@ EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, switch (timestamp) { #if ENABLE_EGL_ANDROID_GET_FRAME_TIMESTAMPS + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: case EGL_REQUESTED_PRESENT_TIME_ANDROID: case EGL_RENDERING_COMPLETE_TIME_ANDROID: case EGL_COMPOSITION_LATCH_TIME_ANDROID: diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt index f24d634963..f946418588 100644 --- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt +++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt @@ -60,26 +60,33 @@ New Procedures and Functions EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId); + EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint numTimestamps, + const EGLint *names, EGLnsecsANDROID *values); + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); - EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface - surface, EGLint timestamp); + EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint timestamp); New Tokens EGL_TIMESTAMPS_ANDROID 0x314D - EGL_REQUESTED_PRESENT_TIME_ANDROID 0x314E - EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F - EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3150 - EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3151 - EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3152 - EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3153 - EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3154 - EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3155 - EGL_DEQUEUE_READY_TIME_ANDROID 0x3156 - EGL_READS_DONE_TIME_ANDROID 0x3157 + EGL_COMPOSITE_DEADLINE_ANDROID 0x314E + EGL_COMPOSITE_INTERVAL_ANDROID 0x314F + EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID 0x3150 + EGL_REQUESTED_PRESENT_TIME_ANDROID 0x3151 + EGL_RENDERING_COMPLETE_TIME_ANDROID 0x3152 + EGL_COMPOSITION_LATCH_TIME_ANDROID 0x3153 + EGL_FIRST_COMPOSITION_START_TIME_ANDROID 0x3154 + EGL_LAST_COMPOSITION_START_TIME_ANDROID 0x3155 + EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID 0x3156 + EGL_DISPLAY_PRESENT_TIME_ANDROID 0x3157 + EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3158 + EGL_DEQUEUE_READY_TIME_ANDROID 0x3159 + EGL_READS_DONE_TIME_ANDROID 0x315A Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6 "Surface Attributes", page 43: @@ -108,6 +115,29 @@ Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) The function + EGLBoolean eglGetCompositorTimingANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint numTimestamps, + const EGLint *names, EGLnsecsANDROID *values); + + allows querying anticipated timestamps and durations related to the + composition and display of a window surface. The values are not associated + with a particular frame and can be retrieved before the first swap. + + The eglGetCompositorTimingANDROID function takes an array of names to + query and returns their values in the corresponding indices of the values + array. The possible names that can be queried are: + - EGL_COMPOSITE_DEADLINE_ANDROID - The timestamp of the next time the + compositor will begin composition. This is effectively the deadline + for when the compositor must receive a newly queued frame. + - EGL_COMPOSITE_INTERVAL_ANDROID - The time delta between subsequent + composition events. + - EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID - The time delta between + the start of composition and the expected present time of that + composition. This can be used to estimate the latency of the + actual present time. + + The function + EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values); @@ -162,12 +192,19 @@ Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors) purpose of display/composition were completed for this frame. Not all implementations may support all of the above timestamp queries. The - function + functions - EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface - surface, EGLint timestamp); + EGLBoolean eglGetCompositorTimingSupportedANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint name); - allows querying which timestamps are supported on the implementation." + and + + EGLBoolean eglGetFrameTimestampsSupportedANDROID(EGLDisplay dpy, + EGLSurface surface, EGLint timestamp); + + allows querying which values are supported by the implementations of + eglGetCompositoTimingANDROID and eglGetFrameTimestampsSupportedANDROID + respectively." Issues @@ -189,3 +226,6 @@ Revision History #4 (Brian Anderson, January 10, 2017) - Use an absolute frameId rather than a relative framesAgo. + +#5 (Brian Anderson, January 13, 2017) + - Add eglGetCompositorTimingANDROID. diff --git a/opengl/specs/README b/opengl/specs/README index 1853214917..0c49023f08 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -20,14 +20,17 @@ for use by Android extensions. 0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop) 0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh) 0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x314E EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3150 EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3151 EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3152 EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3153 EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3154 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3155 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3156 EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3157 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) -0x3158 - 0x315F (unused) +0x314E EGL_COMPOSITE_DEADLINE_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x314F EGL_COMPOSITE_INTERVAL_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3150 EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3151 EGL_REQUESTED_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3152 EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3153 EGL_COMPOSITION_LATCH_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3154 EGL_FIRST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3155 EGL_LAST_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3156 EGL_FIRST_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3157 EGL_DISPLAY_PRESENT_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3158 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x3159 EGL_DEQUEUE_READY_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x315A EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps) +0x315B - 0x315F (unused) diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index e57c19ad2b..41fb48c084 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -155,6 +155,10 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, flinger->getHwComposer().getRefreshPeriod(HWC_DISPLAY_PRIMARY); #endif mFrameTracker.setDisplayRefreshPeriod(displayPeriod); + + CompositorTiming compositorTiming; + flinger->getCompositorTiming(&compositorTiming); + mFrameEventHistory.initializeCompositorTiming(compositorTiming); } void Layer::onFirstRef() { @@ -1856,10 +1860,10 @@ bool Layer::onPreComposition(nsecs_t refreshStartTime) { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } -bool Layer::onPostComposition( - const std::shared_ptr<FenceTime>& glDoneFence, +bool Layer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, - const std::shared_ptr<FenceTime>& retireFence) { + const std::shared_ptr<FenceTime>& retireFence, + const CompositorTiming& compositorTiming) { mAcquireTimeline.updateSignalTimes(); mReleaseTimeline.updateSignalTimes(); @@ -1872,7 +1876,7 @@ bool Layer::onPostComposition( { Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.addPostComposition(mCurrentFrameNumber, - glDoneFence, presentFence); + glDoneFence, presentFence, compositorTiming); mFrameEventHistory.addRetire(mPreviousFrameNumber, retireFence); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 7335be780f..c09b8d92e6 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -296,10 +296,10 @@ public: * called after composition. * returns true if the layer latched a new buffer this frame. */ - bool onPostComposition( - const std::shared_ptr<FenceTime>& glDoneFence, + bool onPostComposition(const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, - const std::shared_ptr<FenceTime>& retireFence); + const std::shared_ptr<FenceTime>& retireFence, + const CompositorTiming& compositorTiming); #ifdef USE_HWC2 // If a buffer was replaced this frame, release the former buffer diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 9e25e0739d..70bd7be457 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -136,7 +136,7 @@ static int64_t getVsyncEventPhaseOffsetNs() { static int64_t vsyncPhaseOffsetNs = getVsyncEventPhaseOffsetNs(); // This is the phase offset at which SurfaceFlinger's composition runs. -static const int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS; +static constexpr int64_t sfVsyncPhaseOffsetNs = SF_VSYNC_EVENT_PHASE_OFFSET_NS; // --------------------------------------------------------------------------- @@ -1095,6 +1095,11 @@ void SurfaceFlinger::onVSyncReceived(int32_t type, nsecs_t timestamp) { } } +void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + *compositorTiming = mCompositorTiming; +} + void SurfaceFlinger::onHotplugReceived(int32_t disp, bool connected) { ALOGV("onHotplugReceived(%d, %s)", disp, connected ? "true" : "false"); if (disp == DisplayDevice::DISPLAY_PRIMARY) { @@ -1145,7 +1150,8 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { case MessageQueue::INVALIDATE: { bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE && - mPreviousPresentFence->getSignalTime() == INT64_MAX; + (mPreviousPresentFence->getSignalTime() == + Fence::SIGNAL_TIME_PENDING); ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); if (mPropagateBackpressure && frameMissed) { signalLayerUpdate(); @@ -1194,7 +1200,7 @@ void SurfaceFlinger::handleMessageRefresh() { setUpHWComposer(); doDebugFlashRegions(); doComposition(); - postComposition(); + postComposition(refreshStartTime); mPreviousPresentFence = mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); @@ -1269,7 +1275,61 @@ void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) } } -void SurfaceFlinger::postComposition() +void SurfaceFlinger::updateCompositorTiming( + nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime) { + // Update queue of past composite+present times and determine the + // most recently known composite to present latency. + mCompositePresentTimes.push({compositeTime, presentFenceTime}); + nsecs_t compositeToPresentLatency = -1; + while (!mCompositePresentTimes.empty()) { + CompositePresentTime& cpt = mCompositePresentTimes.front(); + // Cached values should have been updated before calling this method, + // which helps avoid duplicate syscalls. + nsecs_t displayTime = cpt.display->getCachedSignalTime(); + if (displayTime == Fence::SIGNAL_TIME_PENDING) { + break; + } + compositeToPresentLatency = displayTime - cpt.composite; + mCompositePresentTimes.pop(); + } + + // Don't let mCompositePresentTimes grow unbounded, just in case. + while (mCompositePresentTimes.size() > 16) { + mCompositePresentTimes.pop(); + } + + // Integer division and modulo round toward 0 not -inf, so we need to + // treat negative and positive offsets differently. + nsecs_t idealLatency = (sfVsyncPhaseOffsetNs >= 0) ? + (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) : + ((-sfVsyncPhaseOffsetNs) % vsyncInterval); + + // Snap the latency to a value that removes scheduling jitter from the + // composition and present times, which often have >1ms of jitter. + // Reducing jitter is important if an app attempts to extrapolate + // something (such as user input) to an accurate diasplay time. + // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs + // with (presentLatency % interval). + nsecs_t snappedCompositeToPresentLatency = -1; + if (compositeToPresentLatency >= 0) { + nsecs_t bias = vsyncInterval / 2; + int64_t extraVsyncs = + (compositeToPresentLatency - idealLatency + bias) / + vsyncInterval; + nsecs_t extraLatency = extraVsyncs * vsyncInterval; + snappedCompositeToPresentLatency = idealLatency + extraLatency; + } + + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + mCompositorTiming.deadline = vsyncPhase - idealLatency; + mCompositorTiming.interval = vsyncInterval; + if (snappedCompositeToPresentLatency >= 0) { + mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; + } +} + +void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) { ATRACE_CALL(); ALOGV("postComposition"); @@ -1304,9 +1364,19 @@ void SurfaceFlinger::postComposition() } else { retireFenceTime = &displayFenceTime; } + + nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); + nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); + + // We use the refreshStartTime which might be sampled a little later than + // when we started doing work for this frame, but that should be okay + // since updateCompositorTiming has snapping logic. + updateCompositorTiming( + vsyncPhase, vsyncInterval, refreshStartTime, displayFenceTime); + mDrawingState.traverseInZOrder([&](Layer* layer) { bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime, - *presentFenceTime, *retireFenceTime); + *presentFenceTime, *retireFenceTime, mCompositorTiming); if (frameLatched) { recordBufferingStats(layer->getName().string(), layer->getOccupancyHistory(false)); @@ -1351,9 +1421,8 @@ void SurfaceFlinger::postComposition() if (mHasPoweredOff) { mHasPoweredOff = false; } else { - nsecs_t period = mPrimaryDispSync.getPeriod(); nsecs_t elapsedTime = currentTime - mLastSwapTime; - size_t numPeriods = static_cast<size_t>(elapsedTime / period); + size_t numPeriods = static_cast<size_t>(elapsedTime / vsyncInterval); if (numPeriods < NUM_BUCKETS - 1) { mFrameBuckets[numPeriods] += elapsedTime; } else { @@ -2761,6 +2830,11 @@ void SurfaceFlinger::onInitializeDisplays() { const auto& activeConfig = mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); + + { + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + mCompositorTiming.interval = period; + } } void SurfaceFlinger::initializeDisplays() { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 55735b13b9..743d58bcf3 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -41,6 +41,7 @@ #include <ui/PixelFormat.h> #include <ui/mat4.h> +#include <gui/FrameTimestamps.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> #include <gui/OccupancyTracker.h> @@ -64,7 +65,10 @@ #include "Effects/Daltonizer.h" #include <map> +#include <mutex> +#include <queue> #include <string> +#include <utility> namespace android { @@ -405,7 +409,10 @@ private: Region& dirtyRegion, Region& opaqueRegion); void preComposition(nsecs_t refreshStartTime); - void postComposition(); + void postComposition(nsecs_t refreshStartTime); + void updateCompositorTiming( + nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime); void rebuildLayerStacks(); void setUpHWComposer(); void doComposition(); @@ -426,12 +433,13 @@ private: /* ------------------------------------------------------------------------ * VSync */ - void enableHardwareVsync(); - void resyncToHardwareVsync(bool makeAvailable); - void disableHardwareVsync(bool makeUnavailable); + void enableHardwareVsync(); + void resyncToHardwareVsync(bool makeAvailable); + void disableHardwareVsync(bool makeUnavailable); public: - void resyncWithRateLimit(); + void resyncWithRateLimit(); + void getCompositorTiming(CompositorTiming* compositorTiming); private: /* ------------------------------------------------------------------------ @@ -551,6 +559,17 @@ private: bool mPrimaryHWVsyncEnabled; bool mHWVsyncAvailable; + // protected by mCompositorTimingLock; + mutable std::mutex mCompositeTimingLock; + CompositorTiming mCompositorTiming; + + // Only accessed from the main thread. + struct CompositePresentTime { + nsecs_t composite { -1 }; + std::shared_ptr<FenceTime> display { FenceTime::NO_FENCE }; + }; + std::queue<CompositePresentTime> mCompositePresentTimes; + /* ------------------------------------------------------------------------ * Feature prototyping */ diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index fe9ba9695a..7fcc41a7be 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -1035,6 +1035,11 @@ void SurfaceFlinger::onVSyncReceived(int type, nsecs_t timestamp) { } } +void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + *compositorTiming = mCompositorTiming; +} + void SurfaceFlinger::onHotplugReceived(int type, bool connected) { if (mEventThread == NULL) { // This is a temporary workaround for b/7145521. A non-null pointer @@ -1109,7 +1114,7 @@ void SurfaceFlinger::handleMessageRefresh() { setUpHWComposer(); doDebugFlashRegions(); doComposition(); - postComposition(); + postComposition(refreshStartTime); } void SurfaceFlinger::doDebugFlashRegions() @@ -1166,7 +1171,61 @@ void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) } } -void SurfaceFlinger::postComposition() +void SurfaceFlinger::updateCompositorTiming( + nsecs_t vsyncPhase, nsecs_t vsyncInterval, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime) { + // Update queue of past composite+present times and determine the + // most recently known composite to present latency. + mCompositePresentTimes.push({compositeTime, presentFenceTime}); + nsecs_t compositeToPresentLatency = -1; + while (!mCompositePresentTimes.empty()) { + CompositePresentTime& cpt = mCompositePresentTimes.front(); + // Cached values should have been updated before calling this method, + // which helps avoid duplicate syscalls. + nsecs_t displayTime = cpt.display->getCachedSignalTime(); + if (displayTime == Fence::SIGNAL_TIME_PENDING) { + break; + } + compositeToPresentLatency = displayTime - cpt.composite; + mCompositePresentTimes.pop(); + } + + // Don't let mCompositePresentTimes grow unbounded, just in case. + while (mCompositePresentTimes.size() > 16) { + mCompositePresentTimes.pop(); + } + + // Integer division and modulo round toward 0 not -inf, so we need to + // treat negative and positive offsets differently. + nsecs_t idealLatency = (sfVsyncPhaseOffsetNs >= 0) ? + (vsyncInterval - (sfVsyncPhaseOffsetNs % vsyncInterval)) : + ((-sfVsyncPhaseOffsetNs) % vsyncInterval); + + // Snap the latency to a value that removes scheduling jitter from the + // composition and present times, which often have >1ms of jitter. + // Reducing jitter is important if an app attempts to extrapolate + // something (such as user input) to an accurate diasplay time. + // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs + // with (presentLatency % interval). + nsecs_t snappedCompositeToPresentLatency = -1; + if (compositeToPresentLatency >= 0) { + nsecs_t bias = vsyncInterval / 2; + int64_t extraVsyncs = + (compositeToPresentLatency - idealLatency + bias) / + vsyncInterval; + nsecs_t extraLatency = extraVsyncs * vsyncInterval; + snappedCompositeToPresentLatency = idealLatency + extraLatency; + } + + std::lock_guard<std::mutex> lock(mCompositeTimingLock); + mCompositorTiming.deadline = vsyncPhase - idealLatency; + mCompositorTiming.interval = vsyncInterval; + if (snappedCompositeToPresentLatency >= 0) { + mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; + } +} + +void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) { const HWComposer& hwc = getHwComposer(); const sp<const DisplayDevice> hw(getDefaultDisplayDevice()); @@ -1187,10 +1246,18 @@ void SurfaceFlinger::postComposition() mDisplayTimeline.push(retireFenceTime); mDisplayTimeline.updateSignalTimes(); + nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); + nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); + + // We use the refreshStartTime which might be sampled a little later than + // when we started doing work for this frame, but that should be okay + // since updateCompositorTiming has snapping logic. + updateCompositorTiming( + vsyncPhase, vsyncInterval, refreshStartTime, retireFenceTime); + mDrawingState.traverseInZOrder([&](Layer* layer) { bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime, - presentFenceTime, retireFenceTime); - + presentFenceTime, retireFenceTime, mCompositorTiming); if (frameLatched) { recordBufferingStats(layer->getName().string(), layer->getOccupancyHistory(false)); |