Cache frame event history producer-side.
* Producer maintains a recent history of frames.
* Producer only does a binder call if requested
informatiVon doesn't exist in the cache.
* Consumer sends fences to the producer, which
can be queried for timestamps without a
binder call.
Test: adb shell /data/nativetest/libgui_test/libgui_test
--gtest_filter=*GetFrameTimestamps*
Change-Id: I8a64579407cc2935f5c659462cb227b07ba27e43
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index a919911..4b98cff 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <limits>
+#include <numeric>
namespace android {
@@ -29,25 +30,56 @@
}
// ============================================================================
-// FrameTimestamps
-// ============================================================================
-
-FrameTimestamps::FrameTimestamps(const FrameEvents& events) :
- frameNumber(events.frameNumber),
- postedTime(events.postedTime),
- requestedPresentTime(events.requestedPresentTime),
- acquireTime(events.acquireTime),
- refreshStartTime(events.firstRefreshStartTime),
- glCompositionDoneTime(events.gpuCompositionDoneTime),
- displayPresentTime(events.displayPresentTime),
- displayRetireTime(events.displayRetireTime),
- releaseTime(events.releaseTime) {}
-
-
-// ============================================================================
// FrameEvents
// ============================================================================
+bool FrameEvents::hasPostedInfo() const {
+ return isValidTimestamp(postedTime);
+}
+
+bool FrameEvents::hasRequestedPresentInfo() const {
+ return isValidTimestamp(requestedPresentTime);
+}
+
+bool FrameEvents::hasLatchInfo() const {
+ return isValidTimestamp(latchTime);
+}
+
+bool FrameEvents::hasFirstRefreshStartInfo() const {
+ return isValidTimestamp(firstRefreshStartTime);
+}
+
+bool FrameEvents::hasLastRefreshStartInfo() const {
+ // The last refresh start time may continue to update until a new frame
+ // is latched. We know we have the final value once the release or retire
+ // info is set. See ConsumerFrameEventHistory::addRetire/Release.
+ return addRetireCalled || addReleaseCalled;
+}
+
+bool FrameEvents::hasAcquireInfo() const {
+ return isValidTimestamp(acquireTime) || acquireFence->isValid();
+}
+
+bool FrameEvents::hasGpuCompositionDoneInfo() const {
+ // We may not get a gpuCompositionDone in addPostComposite if
+ // client/gles compositing isn't needed.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayPresentInfo() const {
+ // We may not get a displayPresent in addPostComposite for HWC1.
+ return addPostCompositeCalled;
+}
+
+bool FrameEvents::hasDisplayRetireInfo() const {
+ // We may not get a displayRetire in addRetire for HWC2.
+ return addRetireCalled;
+}
+
+bool FrameEvents::hasReleaseInfo() const {
+ return addReleaseCalled;
+}
+
static void checkFenceForCompletion(sp<Fence>* fence, nsecs_t* dstTime) {
if ((*fence)->isValid()) {
nsecs_t time = (*fence)->getSignalTime();
@@ -156,6 +188,7 @@
} // namespace
+FrameEventHistory::~FrameEventHistory() = default;
FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
auto frame = std::find_if(
@@ -209,82 +242,407 @@
}
}
-void FrameEventHistory::addQueue(const NewFrameEventsEntry& newFrameEntry) {
- // Overwrite all fields of the frame with default values unless set here.
- FrameEvents newTimestamps;
- newTimestamps.frameNumber = newFrameEntry.frameNumber;
- newTimestamps.postedTime = newFrameEntry.postedTime;
- newTimestamps.requestedPresentTime = newFrameEntry.requestedPresentTime;
- newTimestamps.acquireFence = newFrameEntry.acquireFence;
- newTimestamps.valid = true;
- mFrames[mQueueOffset] = newTimestamps;
- mQueueOffset = mQueueOffset + 1;
- if (mQueueOffset >= mFrames.size()) {
- mQueueOffset = 0;
- }
-}
+// ============================================================================
+// ProducerFrameEventHistory
+// ============================================================================
-void FrameEventHistory::addLatch(uint64_t frameNumber, nsecs_t latchTime) {
- FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ProducerFrameEventHistory::~ProducerFrameEventHistory() = default;
+
+void ProducerFrameEventHistory::updateAcquireFence(
+ uint64_t frameNumber, sp<Fence> acquire) {
+ FrameEvents* frame = getFrame(frameNumber, &mAcquireOffset);
if (frame == nullptr) {
- ALOGE("FrameEventHistory::addLatch: Did not find frame.");
- return;
- }
- frame->latchTime = latchTime;
- return;
-}
-
-void FrameEventHistory::addPreComposition(
- uint64_t frameNumber, nsecs_t refreshStartTime) {
- FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
- if (frame == nullptr) {
- ALOGE("FrameEventHistory::addPreComposition: Did not find frame.");
- return;
- }
- frame->lastRefreshStartTime = refreshStartTime;
- if (!isValidTimestamp(frame->firstRefreshStartTime)) {
- frame->firstRefreshStartTime = refreshStartTime;
- }
-}
-
-void FrameEventHistory::addPostComposition(uint64_t frameNumber,
- sp<Fence> gpuCompositionDone, sp<Fence> displayPresent) {
- FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
- if (frame == nullptr) {
- ALOGE("FrameEventHistory::addPostComposition: Did not find frame.");
+ ALOGE("ProducerFrameEventHistory::updateAcquireFence: "
+ "Did not find frame.");
return;
}
- // Only get GPU and present info for the first composite.
- if (!frame->addPostCompositeCalled) {
- frame->addPostCompositeCalled = true;
- frame->gpuCompositionDoneFence = gpuCompositionDone;
- if (!frame->displayPresentFence->isValid()) {
- frame->displayPresentFence = displayPresent;
+ if (acquire->isValid()) {
+ frame->acquireFence = acquire;
+ } else {
+ // If there isn't an acquire fence, assume that buffer was
+ // ready for the consumer when posted.
+ frame->acquireTime = frame->postedTime;
+ }
+}
+
+static void applyFenceDelta(sp<Fence>* dst, const sp<Fence>& src) {
+ if (src->isValid()) {
+ if ((*dst)->isValid()) {
+ ALOGE("applyFenceDelta: Unexpected fence.");
+ }
+ *dst = src;
+ }
+}
+
+void ProducerFrameEventHistory::applyDelta(
+ const FrameEventHistoryDelta& delta) {
+ for (auto& d : delta.mDeltas) {
+ // Avoid out-of-bounds access.
+ if (d.mIndex >= mFrames.size()) {
+ ALOGE("ProducerFrameEventHistory::applyDelta: Bad index.");
+ return;
+ }
+
+ FrameEvents& frame = mFrames[d.mIndex];
+
+ frame.addPostCompositeCalled = d.mAddPostCompositeCalled != 0;
+ frame.addRetireCalled = d.mAddRetireCalled != 0;
+ frame.addReleaseCalled = d.mAddReleaseCalled != 0;
+
+ frame.postedTime = d.mPostedTime;
+ frame.requestedPresentTime = d.mRequestedPresentTime;
+ frame.latchTime = d.mLatchTime;
+ frame.firstRefreshStartTime = d.mFirstRefreshStartTime;
+ frame.lastRefreshStartTime = d.mLastRefreshStartTime;
+
+ if (frame.frameNumber == d.mFrameNumber) {
+ // Existing frame. Merge.
+ // Consumer never sends timestamps of fences, only the fences
+ // themselves, so we never need to update the fence timestamps here.
+ applyFenceDelta(&frame.acquireFence, d.mAcquireFence);
+ applyFenceDelta(
+ &frame.gpuCompositionDoneFence, d.mGpuCompositionDoneFence);
+ applyFenceDelta(&frame.displayPresentFence, d.mDisplayPresentFence);
+ applyFenceDelta(&frame.displayRetireFence, d.mDisplayRetireFence);
+ applyFenceDelta(&frame.releaseFence, d.mReleaseFence);
+ } else {
+ // New frame. Overwrite.
+ frame.frameNumber = d.mFrameNumber;
+
+ frame.gpuCompositionDoneFence = d.mGpuCompositionDoneFence;
+ frame.displayPresentFence = d.mDisplayPresentFence;
+ frame.displayRetireFence = d.mDisplayRetireFence;
+ frame.releaseFence = d.mReleaseFence;
+
+ // Set aquire fence and time at this point.
+ frame.acquireTime = 0;
+ frame.acquireFence = d.mAcquireFence;
+
+ // Reset fence-related timestamps
+ frame.gpuCompositionDoneTime = 0;
+ frame.displayPresentTime = 0;
+ frame.displayRetireTime = 0;
+ frame.releaseTime = 0;
+
+ // The consumer only sends valid frames.
+ frame.valid = true;
}
}
}
-void FrameEventHistory::addRetire(
+
+// ============================================================================
+// ConsumerFrameEventHistory
+// ============================================================================
+
+ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default;
+
+void ConsumerFrameEventHistory::addQueue(const NewFrameEventsEntry& newEntry) {
+ // Overwrite all fields of the frame with default values unless set here.
+ FrameEvents newTimestamps;
+ newTimestamps.frameNumber = newEntry.frameNumber;
+ newTimestamps.postedTime = newEntry.postedTime;
+ newTimestamps.requestedPresentTime = newEntry.requestedPresentTime;
+ newTimestamps.acquireFence = newEntry.acquireFence;
+ newTimestamps.valid = true;
+ mFrames[mQueueOffset] = newTimestamps;
+ mFramesDirty[mQueueOffset].setDirty<FrameEvent::POSTED>();
+ mFramesDirty[mQueueOffset].setDirty<FrameEvent::ACQUIRE>();
+
+ mQueueOffset = (mQueueOffset + 1) % mFrames.size();
+}
+
+void ConsumerFrameEventHistory::addLatch(
+ uint64_t frameNumber, nsecs_t latchTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addLatch: Did not find frame.");
+ return;
+ }
+ frame->latchTime = latchTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LATCH>();
+}
+
+void ConsumerFrameEventHistory::addPreComposition(
+ uint64_t frameNumber, nsecs_t refreshStartTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addPreComposition: "
+ "Did not find frame.");
+ return;
+ }
+ frame->lastRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::LAST_REFRESH_START>();
+ if (!isValidTimestamp(frame->firstRefreshStartTime)) {
+ frame->firstRefreshStartTime = refreshStartTime;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::FIRST_REFRESH_START>();
+ }
+}
+
+void ConsumerFrameEventHistory::addPostComposition(uint64_t frameNumber,
+ sp<Fence> gpuCompositionDone, sp<Fence> displayPresent) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ if (frame == nullptr) {
+ ALOGE("ConsumerFrameEventHistory::addPostComposition: "
+ "Did not find frame.");
+ return;
+ }
+ // Only get GPU and present info for the first composite.
+ if (!frame->addPostCompositeCalled) {
+ frame->addPostCompositeCalled = true;
+ frame->gpuCompositionDoneFence = gpuCompositionDone;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::GL_COMPOSITION_DONE>();
+ if (!frame->displayPresentFence->isValid()) {
+ frame->displayPresentFence = displayPresent;
+ mFramesDirty[mCompositionOffset].setDirty<FrameEvent::DISPLAY_PRESENT>();
+ }
+ }
+}
+
+void ConsumerFrameEventHistory::addRetire(
uint64_t frameNumber, sp<Fence> displayRetire) {
FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
if (frame == nullptr) {
- ALOGE("FrameEventHistory::addRetire: Did not find frame.");
+ ALOGE("ConsumerFrameEventHistory::addRetire: Did not find frame.");
return;
}
frame->addRetireCalled = true;
frame->displayRetireFence = displayRetire;
+ mFramesDirty[mRetireOffset].setDirty<FrameEvent::DISPLAY_RETIRE>();
}
-void FrameEventHistory::addRelease(
+void ConsumerFrameEventHistory::addRelease(
uint64_t frameNumber, sp<Fence> release) {
FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
if (frame == nullptr) {
- ALOGE("FrameEventHistory::addRelease: Did not find frame.");
+ ALOGE("ConsumerFrameEventHistory::addRelease: Did not find frame.");
return;
}
+ frame->addReleaseCalled = true;
frame->releaseFence = release;
+ mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>();
}
+void ConsumerFrameEventHistory::getAndResetDelta(
+ FrameEventHistoryDelta* delta) {
+ delta->mDeltas.reserve(mFramesDirty.size());
+ for (size_t i = 0; i < mFramesDirty.size(); i++) {
+ if (mFramesDirty[i].anyDirty()) {
+ delta->mDeltas.push_back(
+ FrameEventsDelta(i, mFrames[i], mFramesDirty[i]));
+ mFramesDirty[i].reset();
+ }
+ }
+}
+
+
+// ============================================================================
+// FrameEventsDelta
+// ============================================================================
+
+FrameEventsDelta::FrameEventsDelta(
+ size_t index,
+ const FrameEvents& frameTimestamps,
+ const FrameEventDirtyFields& dirtyFields)
+ : mIndex(index),
+ mFrameNumber(frameTimestamps.frameNumber),
+ mAddPostCompositeCalled(frameTimestamps.addPostCompositeCalled),
+ mAddRetireCalled(frameTimestamps.addRetireCalled),
+ mAddReleaseCalled(frameTimestamps.addReleaseCalled),
+ mPostedTime(frameTimestamps.postedTime),
+ mRequestedPresentTime(frameTimestamps.requestedPresentTime),
+ mLatchTime(frameTimestamps.latchTime),
+ mFirstRefreshStartTime(frameTimestamps.firstRefreshStartTime),
+ mLastRefreshStartTime(frameTimestamps.lastRefreshStartTime) {
+ mAcquireFence = dirtyFields.isDirty<FrameEvent::ACQUIRE>() ?
+ frameTimestamps.acquireFence : Fence::NO_FENCE;
+ mGpuCompositionDoneFence =
+ dirtyFields.isDirty<FrameEvent::GL_COMPOSITION_DONE>() ?
+ frameTimestamps.gpuCompositionDoneFence : Fence::NO_FENCE;
+ mDisplayPresentFence = dirtyFields.isDirty<FrameEvent::DISPLAY_PRESENT>() ?
+ frameTimestamps.displayPresentFence : Fence::NO_FENCE;
+ mDisplayRetireFence = dirtyFields.isDirty<FrameEvent::DISPLAY_RETIRE>() ?
+ frameTimestamps.displayRetireFence : Fence::NO_FENCE;
+ mReleaseFence = dirtyFields.isDirty<FrameEvent::RELEASE>() ?
+ frameTimestamps.releaseFence : Fence::NO_FENCE;
+}
+
+size_t FrameEventsDelta::minFlattenedSize() {
+ constexpr size_t min =
+ sizeof(FrameEventsDelta::mFrameNumber) +
+ sizeof(uint8_t) + // mIndex
+ sizeof(uint8_t) + // mAddPostCompositeCalled
+ sizeof(uint8_t) + // mAddRetireCalled
+ sizeof(uint8_t) + // mAddReleaseCalled
+ sizeof(FrameEventsDelta::mPostedTime) +
+ sizeof(FrameEventsDelta::mRequestedPresentTime) +
+ sizeof(FrameEventsDelta::mLatchTime) +
+ sizeof(FrameEventsDelta::mFirstRefreshStartTime) +
+ sizeof(FrameEventsDelta::mLastRefreshStartTime);
+ return min;
+}
+
+// Flattenable implementation
+size_t FrameEventsDelta::getFlattenedSize() const {
+ auto fences = allFences(this);
+ return minFlattenedSize() +
+ std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const sp<Fence>* fence) {
+ return a + (*fence)->getFlattenedSize();
+ });
+}
+
+size_t FrameEventsDelta::getFdCount() const {
+ auto fences = allFences(this);
+ return std::accumulate(fences.begin(), fences.end(), size_t(0),
+ [](size_t a, const sp<Fence>* fence) {
+ return a + (*fence)->getFdCount();
+ });
+}
+
+status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds,
+ size_t& count) const {
+ if (size < getFlattenedSize() || count < getFdCount()) {
+ return NO_MEMORY;
+ }
+
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY ||
+ mIndex > std::numeric_limits<uint8_t>::max()) {
+ return BAD_VALUE;
+ }
+
+ FlattenableUtils::write(buffer, size, mFrameNumber);
+
+ // These are static_cast to uint8_t for alignment.
+ FlattenableUtils::write(buffer, size, static_cast<uint8_t>(mIndex));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddPostCompositeCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddRetireCalled));
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint8_t>(mAddReleaseCalled));
+
+ FlattenableUtils::write(buffer, size, mPostedTime);
+ FlattenableUtils::write(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::write(buffer, size, mLatchTime);
+ FlattenableUtils::write(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::write(buffer, size, mLastRefreshStartTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ status_t status = (*fence)->flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size,
+ int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::read(buffer, size, mFrameNumber);
+
+ // These were written as uint8_t for alignment.
+ uint8_t temp = 0;
+ FlattenableUtils::read(buffer, size, temp);
+ mIndex = temp;
+ if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ FlattenableUtils::read(buffer, size, temp);
+ mAddPostCompositeCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ mAddRetireCalled = static_cast<bool>(temp);
+ FlattenableUtils::read(buffer, size, temp);
+ mAddReleaseCalled = static_cast<bool>(temp);
+
+ FlattenableUtils::read(buffer, size, mPostedTime);
+ FlattenableUtils::read(buffer, size, mRequestedPresentTime);
+ FlattenableUtils::read(buffer, size, mLatchTime);
+ FlattenableUtils::read(buffer, size, mFirstRefreshStartTime);
+ FlattenableUtils::read(buffer, size, mLastRefreshStartTime);
+
+ // Fences
+ for (auto fence : allFences(this)) {
+ *fence = new Fence;
+ status_t status = (*fence)->unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
+// ============================================================================
+// FrameEventHistoryDelta
+// ============================================================================
+
+size_t FrameEventHistoryDelta::minFlattenedSize() {
+ return sizeof(uint32_t);
+}
+
+size_t FrameEventHistoryDelta::getFlattenedSize() const {
+ return minFlattenedSize() +
+ std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFlattenedSize();
+ });
+}
+
+size_t FrameEventHistoryDelta::getFdCount() const {
+ return std::accumulate(mDeltas.begin(), mDeltas.end(), size_t(0),
+ [](size_t a, const FrameEventsDelta& delta) {
+ return a + delta.getFdCount();
+ });
+}
+
+status_t FrameEventHistoryDelta::flatten(
+ void*& buffer, size_t& size, int*& fds, size_t& count) const {
+ if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ if (size < getFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ FlattenableUtils::write(
+ buffer, size, static_cast<uint32_t>(mDeltas.size()));
+ for (auto& d : mDeltas) {
+ status_t status = d.flatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t FrameEventHistoryDelta::unflatten(
+ void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+ if (size < minFlattenedSize()) {
+ return NO_MEMORY;
+ }
+
+ uint32_t deltaCount = 0;
+ FlattenableUtils::read(buffer, size, deltaCount);
+ if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) {
+ return BAD_VALUE;
+ }
+ mDeltas.resize(deltaCount);
+ for (auto& d : mDeltas) {
+ status_t status = d.unflatten(buffer, size, fds, count);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ }
+ return NO_ERROR;
+}
+
+
} // namespace android