Create shared timeline data structure and unit test cases
This change adds the necessary classes for the graphics shared timeline.
Bug: 162889810
Test: libsurfaceflinger_unittest --gtest_filter='FrameTimelineTest*'
Change-Id: I7310a8e0793b2d640d873814bb5a9ba83c76a196
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
new file mode 100644
index 0000000..c95440a
--- /dev/null
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2020 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "FrameTimeline"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FrameTimeline.h"
+#include <android-base/stringprintf.h>
+#include <cinttypes>
+
+namespace android::frametimeline::impl {
+
+using base::StringAppendF;
+
+int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ const int64_t assignedToken = mCurrentToken++;
+ mPredictions[assignedToken] = predictions;
+ mTokens.emplace_back(std::make_pair(assignedToken, systemTime()));
+ flushTokens(systemTime());
+ return assignedToken;
+}
+
+std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ flushTokens(systemTime());
+ auto predictionsIterator = mPredictions.find(token);
+ if (predictionsIterator != mPredictions.end()) {
+ return predictionsIterator->second;
+ }
+ return {};
+}
+
+void TokenManager::flushTokens(nsecs_t flushTime) {
+ for (size_t i = 0; i < mTokens.size(); i++) {
+ if (flushTime - mTokens[i].second >= kMaxRetentionTime) {
+ mPredictions.erase(mTokens[i].first);
+ mTokens.erase(mTokens.begin() + static_cast<int>(i));
+ --i;
+ } else {
+ // Tokens are ordered by time. If i'th token is within the retention time, then the
+ // i+1'th token will also be within retention time.
+ break;
+ }
+ }
+}
+
+SurfaceFrame::SurfaceFrame(const std::string& layerName, PredictionState predictionState,
+ frametimeline::TimelineItem&& predictions)
+ : mLayerName(layerName),
+ mPresentState(PresentState::Unknown),
+ mPredictionState(predictionState),
+ mPredictions(predictions),
+ mActuals({0, 0, 0}),
+ mActualQueueTime(0) {}
+
+void SurfaceFrame::setPresentState(PresentState state) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mPresentState = state;
+}
+
+PredictionState SurfaceFrame::getPredictionState() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPredictionState;
+}
+
+SurfaceFrame::PresentState SurfaceFrame::getPresentState() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mPresentState;
+}
+
+TimelineItem SurfaceFrame::getActuals() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mActuals;
+}
+
+void SurfaceFrame::setActuals(frametimeline::TimelineItem&& actuals) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals = actuals;
+}
+
+void SurfaceFrame::setPresentTime(nsecs_t presentTime) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mActuals.presentTime = presentTime;
+}
+
+void SurfaceFrame::dump(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "Predicted Start Time : %" PRId64 "\n", mPredictions.startTime);
+ StringAppendF(&result, "Actual Start Time : %" PRId64 "\n", mActuals.startTime);
+ StringAppendF(&result, "Actual Queue Time : %" PRId64 "\n", mActualQueueTime);
+ StringAppendF(&result, "Predicted Render Complete Time : %" PRId64 "\n", mPredictions.endTime);
+ StringAppendF(&result, "Actual Render Complete Time : %" PRId64 "\n", mActuals.endTime);
+ StringAppendF(&result, "Predicted Present Time : %" PRId64 "\n", mPredictions.presentTime);
+ StringAppendF(&result, "Actual Present Time : %" PRId64 "\n", mActuals.presentTime);
+}
+
+FrameTimeline::FrameTimeline() : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()) {}
+
+FrameTimeline::DisplayFrame::DisplayFrame()
+ : surfaceFlingerPredictions(TimelineItem()),
+ surfaceFlingerActuals(TimelineItem()),
+ predictionState(PredictionState::None) {
+ this->surfaceFrames.reserve(kNumSurfaceFramesInitial);
+}
+
+std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
+ const std::string& layerName, std::optional<int64_t> token) {
+ if (!token) {
+ return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::None,
+ TimelineItem());
+ }
+ std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token);
+ if (predictions) {
+ return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Valid,
+ std::move(*predictions));
+ }
+ return std::make_unique<impl::SurfaceFrame>(layerName, PredictionState::Expired,
+ TimelineItem());
+}
+
+void FrameTimeline::addSurfaceFrame(
+ std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame,
+ SurfaceFrame::PresentState state) {
+ surfaceFrame->setPresentState(state);
+ std::unique_ptr<impl::SurfaceFrame> implSurfaceFrame(
+ static_cast<impl::SurfaceFrame*>(surfaceFrame.release()));
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->surfaceFrames.push_back(std::move(implSurfaceFrame));
+}
+
+void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) {
+ const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token);
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!prediction) {
+ mCurrentDisplayFrame->predictionState = PredictionState::Expired;
+ } else {
+ mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction;
+ }
+ mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime;
+}
+
+void FrameTimeline::setSfPresent(nsecs_t sfPresentTime,
+ const std::shared_ptr<FenceTime>& presentFence) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime;
+ mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame));
+ flushPendingPresentFences();
+ finalizeCurrentDisplayFrame();
+}
+
+void FrameTimeline::flushPendingPresentFences() {
+ for (size_t i = 0; i < mPendingPresentFences.size(); i++) {
+ const auto& pendingPresentFence = mPendingPresentFences[i];
+ nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+ if (pendingPresentFence.first && pendingPresentFence.first->isValid()) {
+ signalTime = pendingPresentFence.first->getSignalTime();
+ if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+ continue;
+ }
+ }
+ if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+ auto& displayFrame = pendingPresentFence.second;
+ displayFrame->surfaceFlingerActuals.presentTime = signalTime;
+ for (auto& surfaceFrame : displayFrame->surfaceFrames) {
+ if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) {
+ // Only presented SurfaceFrames need to be updated
+ surfaceFrame->setPresentTime(signalTime);
+ }
+ }
+ }
+
+ mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
+ --i;
+ }
+}
+
+void FrameTimeline::finalizeCurrentDisplayFrame() {
+ while (mDisplayFrames.size() >= kMaxDisplayFrames) {
+ // We maintain only a fixed number of frames' data. Pop older frames
+ mDisplayFrames.pop_front();
+ }
+ mDisplayFrames.push_back(mCurrentDisplayFrame);
+ mCurrentDisplayFrame.reset();
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>();
+}
+
+void FrameTimeline::dump(std::string& result) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size());
+ for (const auto& displayFrame : mDisplayFrames) {
+ StringAppendF(&result, "---Display Frame---\n");
+ StringAppendF(&result, "Predicted SF wake time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerPredictions.startTime);
+ StringAppendF(&result, "Actual SF wake time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerActuals.startTime);
+ StringAppendF(&result, "Predicted SF Complete time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerPredictions.endTime);
+ StringAppendF(&result, "Actual SF Complete time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerActuals.endTime);
+ StringAppendF(&result, "Predicted Present time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerPredictions.presentTime);
+ StringAppendF(&result, "Actual Present time : %" PRId64 "\n",
+ displayFrame->surfaceFlingerActuals.presentTime);
+ for (size_t i = 0; i < displayFrame->surfaceFrames.size(); i++) {
+ StringAppendF(&result, "Surface frame - %" PRId32 "\n", (int)i);
+ displayFrame->surfaceFrames[i]->dump(result);
+ }
+ }
+}
+
+} // namespace android::frametimeline::impl