Track frame events incrementally and per layer.
* Replaces FenceTracker, which was owned by SurfaceFlinger,
with FrameEventHistory, which is owned by Layer.
* Updates FrameEventHistory as events occur.
* Changes SurfaceFlinger flag "--fences" to
"--frame-events".
Test: adb shell /data/nativetest/libgui_test/libgui_test
--gtest_filter=*GetFrameTimestamps*
Change-Id: I868c2ef93964656d7e41848243433499e7f45fe7
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
new file mode 100644
index 0000000..a919911
--- /dev/null
+++ b/libs/gui/FrameTimestamps.cpp
@@ -0,0 +1,290 @@
+/*
+* Copyright 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include <gui/FrameTimestamps.h>
+
+#include <inttypes.h>
+#include <utils/String8.h>
+
+#include <algorithm>
+#include <limits>
+
+namespace android {
+
+static inline bool isValidTimestamp(nsecs_t time) {
+ return time > 0 && time < INT64_MAX;
+}
+
+// ============================================================================
+// 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
+// ============================================================================
+
+static void checkFenceForCompletion(sp<Fence>* fence, nsecs_t* dstTime) {
+ if ((*fence)->isValid()) {
+ nsecs_t time = (*fence)->getSignalTime();
+ if (isValidTimestamp(time)) {
+ *dstTime = time;
+ *fence = Fence::NO_FENCE;
+ }
+ }
+}
+
+void FrameEvents::checkFencesForCompletion() {
+ checkFenceForCompletion(&acquireFence, &acquireTime);
+ checkFenceForCompletion(&gpuCompositionDoneFence, &gpuCompositionDoneTime);
+ checkFenceForCompletion(&displayPresentFence, &displayPresentTime);
+ checkFenceForCompletion(&displayRetireFence, &displayRetireTime);
+ checkFenceForCompletion(&releaseFence, &releaseTime);
+}
+
+void FrameEvents::dump(String8& outString) const
+{
+ if (!valid) {
+ return;
+ }
+
+ outString.appendFormat("-- Frame %" PRIu64 "\n", frameNumber);
+ outString.appendFormat("--- Posted \t%" PRId64 "\n", postedTime);
+ outString.appendFormat("--- Req. Present\t%" PRId64 "\n", requestedPresentTime);
+
+ outString.appendFormat("--- Latched \t");
+ if (isValidTimestamp(latchTime)) {
+ outString.appendFormat("%" PRId64 "\n", latchTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (First)\t");
+ if (isValidTimestamp(firstRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", firstRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Refresh (Last)\t");
+ if (isValidTimestamp(lastRefreshStartTime)) {
+ outString.appendFormat("%" PRId64 "\n", lastRefreshStartTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- Acquire \t");
+ if (isValidTimestamp(acquireTime)) {
+ outString.appendFormat("%" PRId64 "\n", acquireTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+
+ outString.appendFormat("--- GPU Composite Done\t");
+ if (isValidTimestamp(gpuCompositionDoneTime)) {
+ outString.appendFormat("%" PRId64 "\n", gpuCompositionDoneTime);
+ } else if (!addPostCompositeCalled || gpuCompositionDoneFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Display Present\t");
+ if (isValidTimestamp(displayPresentTime)) {
+ outString.appendFormat("%" PRId64 "\n", displayPresentTime);
+ } else if (!addPostCompositeCalled || displayPresentFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Display Retire\t");
+ if (isValidTimestamp(displayRetireTime)) {
+ outString.appendFormat("%" PRId64 "\n", displayRetireTime);
+ } else if (!addRetireCalled || displayRetireFence->isValid()) {
+ outString.appendFormat("Pending\n");
+ } else {
+ outString.appendFormat("N/A\n");
+ }
+
+ outString.appendFormat("--- Release \t");
+ if (isValidTimestamp(releaseTime)) {
+ outString.appendFormat("%" PRId64 "\n", releaseTime);
+ } else {
+ outString.appendFormat("Pending\n");
+ }
+}
+
+
+// ============================================================================
+// FrameEventHistory
+// ============================================================================
+
+namespace {
+
+struct FrameNumberEqual {
+ FrameNumberEqual(uint64_t frameNumber) : mFrameNumber(frameNumber) {}
+ bool operator()(const FrameEvents& frame) {
+ return frame.valid && mFrameNumber == frame.frameNumber;
+ }
+ const uint64_t mFrameNumber;
+};
+
+} // namespace
+
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) {
+ auto frame = std::find_if(
+ mFrames.begin(), mFrames.end(), FrameNumberEqual(frameNumber));
+ return frame == mFrames.end() ? nullptr : &(*frame);
+}
+
+FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber, size_t* iHint) {
+ *iHint = std::min(*iHint, mFrames.size());
+ auto hint = mFrames.begin() + *iHint;
+ auto frame = std::find_if(
+ hint, mFrames.end(), FrameNumberEqual(frameNumber));
+ if (frame == mFrames.end()) {
+ frame = std::find_if(
+ mFrames.begin(), hint, FrameNumberEqual(frameNumber));
+ if (frame == hint) {
+ return nullptr;
+ }
+ }
+ *iHint = static_cast<size_t>(std::distance(mFrames.begin(), frame));
+ return &(*frame);
+}
+
+void FrameEventHistory::checkFencesForCompletion() {
+ for (auto& frame : mFrames) {
+ frame.checkFencesForCompletion();
+ }
+}
+
+// Uses !|valid| as the MSB.
+static bool FrameNumberLessThan(
+ const FrameEvents& lhs, const FrameEvents& rhs) {
+ if (lhs.valid == rhs.valid) {
+ return lhs.frameNumber < rhs.frameNumber;
+ }
+ return lhs.valid;
+}
+
+void FrameEventHistory::dump(String8& outString) const {
+ auto earliestFrame = std::min_element(
+ mFrames.begin(), mFrames.end(), &FrameNumberLessThan);
+ if (!earliestFrame->valid) {
+ outString.appendFormat("-- N/A\n");
+ return;
+ }
+ for (auto frame = earliestFrame; frame != mFrames.end(); ++frame) {
+ frame->dump(outString);
+ }
+ for (auto frame = mFrames.begin(); frame != earliestFrame; ++frame) {
+ frame->dump(outString);
+ }
+}
+
+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;
+ }
+}
+
+void FrameEventHistory::addLatch(uint64_t frameNumber, nsecs_t latchTime) {
+ FrameEvents* frame = getFrame(frameNumber, &mCompositionOffset);
+ 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.");
+ 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;
+ }
+ }
+}
+
+void FrameEventHistory::addRetire(
+ uint64_t frameNumber, sp<Fence> displayRetire) {
+ FrameEvents* frame = getFrame(frameNumber, &mRetireOffset);
+ if (frame == nullptr) {
+ ALOGE("FrameEventHistory::addRetire: Did not find frame.");
+ return;
+ }
+ frame->addRetireCalled = true;
+ frame->displayRetireFence = displayRetire;
+}
+
+void FrameEventHistory::addRelease(
+ uint64_t frameNumber, sp<Fence> release) {
+ FrameEvents* frame = getFrame(frameNumber, &mReleaseOffset);
+ if (frame == nullptr) {
+ ALOGE("FrameEventHistory::addRelease: Did not find frame.");
+ return;
+ }
+ frame->releaseFence = release;
+}
+
+} // namespace android