Loading services/surfaceflinger/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ cc_defaults { }, static_libs: [ "libcompositionengine", "libframetimeline", "libperfetto_client_experimental", "librenderengine", "libserviceutils", Loading services/surfaceflinger/FrameTimeline/Android.bp 0 → 100644 +16 −0 Original line number Diff line number Diff line cc_library_static { name: "libframetimeline", defaults: ["surfaceflinger_defaults"], srcs: [ "FrameTimeline.cpp", ], shared_libs: [ "libbase", "libcutils", "liblog", "libgui", "libui", "libutils", ], export_include_dirs: ["."], } services/surfaceflinger/FrameTimeline/FrameTimeline.cpp 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * 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 services/surfaceflinger/FrameTimeline/FrameTimeline.h 0 → 100644 +232 −0 Original line number Diff line number Diff line /* * 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. */ #pragma once #include <deque> #include <mutex> #include <ui/FenceTime.h> #include <utils/RefBase.h> #include <utils/Timers.h> namespace android::frametimeline { class FrameTimelineTest; /* * Collection of timestamps that can be used for both predictions and actual times. */ struct TimelineItem { TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0, const nsecs_t presentTime = 0) : startTime(startTime), endTime(endTime), presentTime(presentTime) {} nsecs_t startTime; nsecs_t endTime; nsecs_t presentTime; }; /* * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It * saves these predictions for a short period of time and returns the predictions for a given token, * if it hasn't expired. */ class TokenManager { public: virtual ~TokenManager() = default; // Generates a token for the given set of predictions. Stores the predictions for 120ms and // destroys it later. virtual int64_t generateTokenForPredictions(TimelineItem&& prediction); }; enum class PredictionState { Valid, // Predictions obtained successfully from the TokenManager Expired, // TokenManager no longer has the predictions None, // Predictions are either not present or didn't come from TokenManager }; /* * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame * from the app */ class SurfaceFrame { public: enum class PresentState { Presented, // Buffer was latched and presented by SurfaceFlinger Dropped, // Buffer was dropped by SurfaceFlinger Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet }; virtual ~SurfaceFrame() = default; virtual TimelineItem getPredictions() = 0; virtual TimelineItem getActuals() = 0; virtual PresentState getPresentState() = 0; virtual PredictionState getPredictionState() = 0; virtual void setPresentState(PresentState state) = 0; virtual void setActuals(TimelineItem&& actuals) = 0; // There is no prediction for Queue time and it is not a part of TimelineItem. Set it // separately. virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0; }; /* * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were * presented */ class FrameTimeline { public: virtual ~FrameTimeline() = default; virtual TokenManager& getTokenManager() = 0; // Create a new surface frame, set the predictions based on a token and return it to the caller. // Sets the PredictionState of SurfaceFrame. virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) = 0; // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // composited into one display frame. virtual void addSurfaceFrame(std::unique_ptr<SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) = 0; // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on // the token and sets the actualSfWakeTime for the current DisplayFrame. virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0; // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in // that vsync. virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) = 0; }; namespace impl { using namespace std::chrono_literals; class TokenManager : public android::frametimeline::TokenManager { public: TokenManager() : mCurrentToken(0) {} ~TokenManager() = default; int64_t generateTokenForPredictions(TimelineItem&& predictions) override; std::optional<TimelineItem> getPredictionsForToken(int64_t token); private: // Friend class for testing friend class android::frametimeline::FrameTimelineTest; void flushTokens(nsecs_t flushTime) REQUIRES(mMutex); std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex); std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex); int64_t mCurrentToken GUARDED_BY(mMutex); std::mutex mMutex; static constexpr nsecs_t kMaxRetentionTime = std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count(); }; class SurfaceFrame : public android::frametimeline::SurfaceFrame { public: SurfaceFrame(const std::string& layerName, PredictionState predictionState, TimelineItem&& predictions); ~SurfaceFrame() = default; TimelineItem getPredictions() override { return mPredictions; }; TimelineItem getActuals() override; PresentState getPresentState() override; PredictionState getPredictionState() override; void setActuals(TimelineItem&& actuals) override; void setActualQueueTime(nsecs_t actualQueueTime) override { mActualQueueTime = actualQueueTime; }; void setPresentState(PresentState state) override; void setPresentTime(nsecs_t presentTime); void dump(std::string& result); private: const std::string mLayerName; PresentState mPresentState GUARDED_BY(mMutex); PredictionState mPredictionState GUARDED_BY(mMutex); const TimelineItem mPredictions; TimelineItem mActuals GUARDED_BY(mMutex); nsecs_t mActualQueueTime; std::mutex mMutex; }; class FrameTimeline : public android::frametimeline::FrameTimeline { public: FrameTimeline(); ~FrameTimeline() = default; frametimeline::TokenManager& getTokenManager() override { return mTokenManager; } std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) override; void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) override; void dump(std::string& result); private: // Friend class for testing friend class android::frametimeline::FrameTimelineTest; /* * DisplayFrame should be used only internally within FrameTimeline. */ struct DisplayFrame { DisplayFrame(); /* Usage of TimelineItem w.r.t SurfaceFlinger * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates * endTime Time when SurfaceFlinger sends a composited frame to Display * presentTime Time when the composited frame was presented on screen */ TimelineItem surfaceFlingerPredictions; TimelineItem surfaceFlingerActuals; // Collection of predictions and actual values sent over by Layers std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; PredictionState predictionState; }; void flushPendingPresentFences() REQUIRES(mMutex); void finalizeCurrentDisplayFrame() REQUIRES(mMutex); // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex); std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>> mPendingPresentFences GUARDED_BY(mMutex); std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex); TokenManager mTokenManager; std::mutex mMutex; static constexpr uint32_t kMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // number doesn't represent any bounds on the number of surface frames that can go in a display // frame, this is a good starting size for the vector so that we can avoid the internal vector // resizing that happens with push_back. static constexpr uint32_t kNumSurfaceFramesInitial = 10; }; } // namespace impl } // namespace android::frametimeline services/surfaceflinger/SurfaceFlinger.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,7 @@ #include "DisplayRenderArea.h" #include "EffectLayer.h" #include "Effects/Daltonizer.h" #include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" #include "Layer.h" #include "LayerRenderArea.h" Loading Loading @@ -332,6 +333,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mInterceptor(mFactory.createSurfaceInterceptor(this)), mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(std::make_unique<FrameTracer>()), mFrameTimeline(std::make_shared<frametimeline::impl::FrameTimeline>()), mEventQueue(mFactory.createMessageQueue()), mCompositionEngine(mFactory.createCompositionEngine()), mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)), Loading Loading
services/surfaceflinger/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ cc_defaults { }, static_libs: [ "libcompositionengine", "libframetimeline", "libperfetto_client_experimental", "librenderengine", "libserviceutils", Loading
services/surfaceflinger/FrameTimeline/Android.bp 0 → 100644 +16 −0 Original line number Diff line number Diff line cc_library_static { name: "libframetimeline", defaults: ["surfaceflinger_defaults"], srcs: [ "FrameTimeline.cpp", ], shared_libs: [ "libbase", "libcutils", "liblog", "libgui", "libui", "libutils", ], export_include_dirs: ["."], }
services/surfaceflinger/FrameTimeline/FrameTimeline.cpp 0 → 100644 +226 −0 Original line number Diff line number Diff line /* * 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
services/surfaceflinger/FrameTimeline/FrameTimeline.h 0 → 100644 +232 −0 Original line number Diff line number Diff line /* * 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. */ #pragma once #include <deque> #include <mutex> #include <ui/FenceTime.h> #include <utils/RefBase.h> #include <utils/Timers.h> namespace android::frametimeline { class FrameTimelineTest; /* * Collection of timestamps that can be used for both predictions and actual times. */ struct TimelineItem { TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0, const nsecs_t presentTime = 0) : startTime(startTime), endTime(endTime), presentTime(presentTime) {} nsecs_t startTime; nsecs_t endTime; nsecs_t presentTime; }; /* * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It * saves these predictions for a short period of time and returns the predictions for a given token, * if it hasn't expired. */ class TokenManager { public: virtual ~TokenManager() = default; // Generates a token for the given set of predictions. Stores the predictions for 120ms and // destroys it later. virtual int64_t generateTokenForPredictions(TimelineItem&& prediction); }; enum class PredictionState { Valid, // Predictions obtained successfully from the TokenManager Expired, // TokenManager no longer has the predictions None, // Predictions are either not present or didn't come from TokenManager }; /* * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame * from the app */ class SurfaceFrame { public: enum class PresentState { Presented, // Buffer was latched and presented by SurfaceFlinger Dropped, // Buffer was dropped by SurfaceFlinger Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet }; virtual ~SurfaceFrame() = default; virtual TimelineItem getPredictions() = 0; virtual TimelineItem getActuals() = 0; virtual PresentState getPresentState() = 0; virtual PredictionState getPredictionState() = 0; virtual void setPresentState(PresentState state) = 0; virtual void setActuals(TimelineItem&& actuals) = 0; // There is no prediction for Queue time and it is not a part of TimelineItem. Set it // separately. virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0; }; /* * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were * presented */ class FrameTimeline { public: virtual ~FrameTimeline() = default; virtual TokenManager& getTokenManager() = 0; // Create a new surface frame, set the predictions based on a token and return it to the caller. // Sets the PredictionState of SurfaceFrame. virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) = 0; // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be // composited into one display frame. virtual void addSurfaceFrame(std::unique_ptr<SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) = 0; // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on // the token and sets the actualSfWakeTime for the current DisplayFrame. virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime) = 0; // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in // that vsync. virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) = 0; }; namespace impl { using namespace std::chrono_literals; class TokenManager : public android::frametimeline::TokenManager { public: TokenManager() : mCurrentToken(0) {} ~TokenManager() = default; int64_t generateTokenForPredictions(TimelineItem&& predictions) override; std::optional<TimelineItem> getPredictionsForToken(int64_t token); private: // Friend class for testing friend class android::frametimeline::FrameTimelineTest; void flushTokens(nsecs_t flushTime) REQUIRES(mMutex); std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex); std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex); int64_t mCurrentToken GUARDED_BY(mMutex); std::mutex mMutex; static constexpr nsecs_t kMaxRetentionTime = std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count(); }; class SurfaceFrame : public android::frametimeline::SurfaceFrame { public: SurfaceFrame(const std::string& layerName, PredictionState predictionState, TimelineItem&& predictions); ~SurfaceFrame() = default; TimelineItem getPredictions() override { return mPredictions; }; TimelineItem getActuals() override; PresentState getPresentState() override; PredictionState getPredictionState() override; void setActuals(TimelineItem&& actuals) override; void setActualQueueTime(nsecs_t actualQueueTime) override { mActualQueueTime = actualQueueTime; }; void setPresentState(PresentState state) override; void setPresentTime(nsecs_t presentTime); void dump(std::string& result); private: const std::string mLayerName; PresentState mPresentState GUARDED_BY(mMutex); PredictionState mPredictionState GUARDED_BY(mMutex); const TimelineItem mPredictions; TimelineItem mActuals GUARDED_BY(mMutex); nsecs_t mActualQueueTime; std::mutex mMutex; }; class FrameTimeline : public android::frametimeline::FrameTimeline { public: FrameTimeline(); ~FrameTimeline() = default; frametimeline::TokenManager& getTokenManager() override { return mTokenManager; } std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( const std::string& layerName, std::optional<int64_t> token) override; void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, SurfaceFrame::PresentState state) override; void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) override; void dump(std::string& result); private: // Friend class for testing friend class android::frametimeline::FrameTimelineTest; /* * DisplayFrame should be used only internally within FrameTimeline. */ struct DisplayFrame { DisplayFrame(); /* Usage of TimelineItem w.r.t SurfaceFlinger * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates * endTime Time when SurfaceFlinger sends a composited frame to Display * presentTime Time when the composited frame was presented on screen */ TimelineItem surfaceFlingerPredictions; TimelineItem surfaceFlingerActuals; // Collection of predictions and actual values sent over by Layers std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; PredictionState predictionState; }; void flushPendingPresentFences() REQUIRES(mMutex); void finalizeCurrentDisplayFrame() REQUIRES(mMutex); // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex); std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>> mPendingPresentFences GUARDED_BY(mMutex); std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex); TokenManager mTokenManager; std::mutex mMutex; static constexpr uint32_t kMaxDisplayFrames = 64; // The initial container size for the vector<SurfaceFrames> inside display frame. Although this // number doesn't represent any bounds on the number of surface frames that can go in a display // frame, this is a good starting size for the vector so that we can avoid the internal vector // resizing that happens with push_back. static constexpr uint32_t kNumSurfaceFramesInitial = 10; }; } // namespace impl } // namespace android::frametimeline
services/surfaceflinger/SurfaceFlinger.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,7 @@ #include "DisplayRenderArea.h" #include "EffectLayer.h" #include "Effects/Daltonizer.h" #include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" #include "Layer.h" #include "LayerRenderArea.h" Loading Loading @@ -332,6 +333,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mInterceptor(mFactory.createSurfaceInterceptor(this)), mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(std::make_unique<FrameTracer>()), mFrameTimeline(std::make_shared<frametimeline::impl::FrameTimeline>()), mEventQueue(mFactory.createMessageQueue()), mCompositionEngine(mFactory.createCompositionEngine()), mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)), Loading