summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gui/FrameTimestamps.h27
-rw-r--r--include/gui/Surface.h11
-rw-r--r--libs/gui/FrameTimestamps.cpp48
-rw-r--r--libs/gui/Surface.cpp81
-rw-r--r--libs/gui/tests/Surface_test.cpp307
-rw-r--r--opengl/include/EGL/eglext.h31
-rw-r--r--opengl/libs/EGL/eglApi.cpp106
-rw-r--r--opengl/specs/EGL_ANDROID_get_frame_timestamps.txt72
-rw-r--r--opengl/specs/README25
-rw-r--r--services/surfaceflinger/Layer.cpp12
-rw-r--r--services/surfaceflinger/Layer.h6
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp88
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h29
-rw-r--r--services/surfaceflinger/SurfaceFlinger_hwc1.cpp75
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));