diff options
Diffstat (limited to 'services/surfaceflinger/Scheduler/LayerInfo.cpp')
-rw-r--r-- | services/surfaceflinger/Scheduler/LayerInfo.cpp | 308 |
1 files changed, 292 insertions, 16 deletions
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 6d9dd43d9a..989bf4ef19 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * 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. @@ -14,36 +14,312 @@ * limitations under the License. */ +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextra" + +// #define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + #include "LayerInfo.h" #include <algorithm> #include <utility> +#include <cutils/compiler.h> +#include <cutils/trace.h> + +#undef LOG_TAG +#define LOG_TAG "LayerInfo" + namespace android::scheduler { -LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate) - : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {} +const RefreshRateConfigs* LayerInfo::sRefreshRateConfigs = nullptr; +bool LayerInfo::sTraceEnabled = false; + +LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid, + LayerHistory::LayerVoteType defaultVote) + : mName(name), + mOwnerUid(ownerUid), + mDefaultVote(defaultVote), + mLayerVote({defaultVote, Fps(0.0f)}), + mRefreshRateHistory(name) {} -void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) { +void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType, + bool pendingModeChange, LayerProps props) { lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0)); - // Buffers can come with a present time far in the future. That keeps them relevant. mLastUpdatedTime = std::max(lastPresentTime, now); - mPresentTimeHistory.insertPresentTime(mLastUpdatedTime); + mLayerProps = props; + switch (updateType) { + case LayerUpdateType::AnimationTX: + mLastAnimationTime = std::max(lastPresentTime, now); + break; + case LayerUpdateType::SetFrameRate: + case LayerUpdateType::Buffer: + FrameTimeData frameTime = {.presentTime = lastPresentTime, + .queueTime = mLastUpdatedTime, + .pendingModeChange = pendingModeChange}; + mFrameTimes.push_back(frameTime); + if (mFrameTimes.size() > HISTORY_SIZE) { + mFrameTimes.pop_front(); + } + break; + } +} + +bool LayerInfo::isFrameTimeValid(const FrameTimeData& frameTime) const { + return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>( + mFrameTimeValidSince.time_since_epoch()) + .count(); +} + +bool LayerInfo::isFrequent(nsecs_t now) const { + // If we know nothing about this layer we consider it as frequent as it might be the start + // of an animation. + if (mFrameTimes.size() < kFrequentLayerWindowSize) { + return true; + } + + // Find the first active frame + auto it = mFrameTimes.begin(); + for (; it != mFrameTimes.end(); ++it) { + if (it->queueTime >= getActiveLayerThreshold(now)) { + break; + } + } + + const auto numFrames = std::distance(it, mFrameTimes.end()); + if (numFrames < kFrequentLayerWindowSize) { + return false; + } + + // Layer is considered frequent if the average frame rate is higher than the threshold + const auto totalTime = mFrameTimes.back().queueTime - it->queueTime; + return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)) + .greaterThanOrEqualWithMargin(kMinFpsForFrequentLayer); +} + +bool LayerInfo::isAnimating(nsecs_t now) const { + return mLastAnimationTime >= getActiveLayerThreshold(now); +} + +bool LayerInfo::hasEnoughDataForHeuristic() const { + // The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates + if (mFrameTimes.size() < 2) { + ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size()); + return false; + } + + if (!isFrameTimeValid(mFrameTimes.front())) { + ALOGV("stale frames still captured"); + return false; + } + + const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime; + if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) { + ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(), + totalDuration / 1e9f); + return false; + } + + return true; +} + +std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { + // Ignore frames captured during a mode change + const bool isDuringModeChange = + std::any_of(mFrameTimes.begin(), mFrameTimes.end(), + [](const auto& frame) { return frame.pendingModeChange; }); + if (isDuringModeChange) { + return std::nullopt; + } + + const bool isMissingPresentTime = + std::any_of(mFrameTimes.begin(), mFrameTimes.end(), + [](auto frame) { return frame.presentTime == 0; }); + if (isMissingPresentTime && !mLastRefreshRate.reported.isValid()) { + // If there are no presentation timestamps and we haven't calculated + // one in the past then we can't calculate the refresh rate + return std::nullopt; + } + + // Calculate the average frame time based on presentation timestamps. If those + // doesn't exist, we look at the time the buffer was queued only. We can do that only if + // we calculated a refresh rate based on presentation timestamps in the past. The reason + // we look at the queue time is to handle cases where hwui attaches presentation timestamps + // when implementing render ahead for specific refresh rates. When hwui no longer provides + // presentation timestamps we look at the queue time to see if the current refresh rate still + // matches the content. + + auto getFrameTime = isMissingPresentTime ? [](FrameTimeData data) { return data.queueTime; } + : [](FrameTimeData data) { return data.presentTime; }; + + nsecs_t totalDeltas = 0; + int numDeltas = 0; + auto prevFrame = mFrameTimes.begin(); + for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) { + const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame); + if (currDelta < kMinPeriodBetweenFrames) { + // Skip this frame, but count the delta into the next frame + continue; + } + + prevFrame = it; + + if (currDelta > kMaxPeriodBetweenFrames) { + // Skip this frame and the current delta. + continue; + } + + totalDeltas += currDelta; + numDeltas++; + } + + if (numDeltas == 0) { + return std::nullopt; + } - if (mLastPresentTime == 0) { - // First frame - mLastPresentTime = lastPresentTime; - return; + const auto averageFrameTime = static_cast<double>(totalDeltas) / static_cast<double>(numDeltas); + return static_cast<nsecs_t>(averageFrameTime); +} + +std::optional<Fps> LayerInfo::calculateRefreshRateIfPossible(nsecs_t now) { + static constexpr float MARGIN = 1.0f; // 1Hz + if (!hasEnoughDataForHeuristic()) { + ALOGV("Not enough data"); + return std::nullopt; + } + + const auto averageFrameTime = calculateAverageFrameTime(); + if (averageFrameTime.has_value()) { + const auto refreshRate = Fps::fromPeriodNsecs(*averageFrameTime); + const bool refreshRateConsistent = mRefreshRateHistory.add(refreshRate, now); + if (refreshRateConsistent) { + const auto knownRefreshRate = + sRefreshRateConfigs->findClosestKnownFrameRate(refreshRate); + + // To avoid oscillation, use the last calculated refresh rate if it is + // close enough + if (std::abs(mLastRefreshRate.calculated.getValue() - refreshRate.getValue()) > + MARGIN && + !mLastRefreshRate.reported.equalsWithMargin(knownRefreshRate)) { + mLastRefreshRate.calculated = refreshRate; + mLastRefreshRate.reported = knownRefreshRate; + } + + ALOGV("%s %s rounded to nearest known frame rate %s", mName.c_str(), + to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str()); + } else { + ALOGV("%s Not stable (%s) returning last known frame rate %s", mName.c_str(), + to_string(refreshRate).c_str(), to_string(mLastRefreshRate.reported).c_str()); + } + } + + return mLastRefreshRate.reported.isValid() ? std::make_optional(mLastRefreshRate.reported) + : std::nullopt; +} + +LayerInfo::LayerVote LayerInfo::getRefreshRateVote(nsecs_t now) { + if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { + ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type)); + return mLayerVote; + } + + if (isAnimating(now)) { + ALOGV("%s is animating", mName.c_str()); + mLastRefreshRate.animatingOrInfrequent = true; + return {LayerHistory::LayerVoteType::Max, Fps(0.0f)}; + } + + if (!isFrequent(now)) { + ALOGV("%s is infrequent", mName.c_str()); + mLastRefreshRate.animatingOrInfrequent = true; + // Infrequent layers vote for mininal refresh rate for + // battery saving purposes and also to prevent b/135718869. + return {LayerHistory::LayerVoteType::Min, Fps(0.0f)}; + } + + // If the layer was previously tagged as animating or infrequent, we clear + // the history as it is likely the layer just changed its behavior + // and we should not look at stale data + if (mLastRefreshRate.animatingOrInfrequent) { + clearHistory(now); + } + + auto refreshRate = calculateRefreshRateIfPossible(now); + if (refreshRate.has_value()) { + ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); + return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; + } + + ALOGV("%s Max (can't resolve refresh rate)", mName.c_str()); + return {LayerHistory::LayerVoteType::Max, Fps(0.0f)}; +} + +const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const { + if (mTraceTags.count(type) == 0) { + const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type); + mTraceTags.emplace(type, tag); + } + + return mTraceTags.at(type).c_str(); +} + +LayerInfo::RefreshRateHistory::HeuristicTraceTagData +LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const { + const std::string prefix = "LFPS "; + const std::string suffix = "Heuristic "; + return {.min = prefix + mName + suffix + "min", + .max = prefix + mName + suffix + "max", + .consistent = prefix + mName + suffix + "consistent", + .average = prefix + mName + suffix + "average"}; +} + +void LayerInfo::RefreshRateHistory::clear() { + mRefreshRates.clear(); +} + +bool LayerInfo::RefreshRateHistory::add(Fps refreshRate, nsecs_t now) { + mRefreshRates.push_back({refreshRate, now}); + while (mRefreshRates.size() >= HISTORY_SIZE || + now - mRefreshRates.front().timestamp > HISTORY_DURATION.count()) { + mRefreshRates.pop_front(); } - const nsecs_t period = lastPresentTime - mLastPresentTime; - mLastPresentTime = lastPresentTime; - // Ignore time diff that are too high - those are stale values - if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return; + if (CC_UNLIKELY(sTraceEnabled)) { + if (!mHeuristicTraceTagData.has_value()) { + mHeuristicTraceTagData = makeHeuristicTraceTagData(); + } - const float fps = std::min(1e9f / period, mHighRefreshRate); - mRefreshRateHistory.insertRefreshRate(fps); + ATRACE_INT(mHeuristicTraceTagData->average.c_str(), refreshRate.getIntValue()); + } + + return isConsistent(); +} + +bool LayerInfo::RefreshRateHistory::isConsistent() const { + if (mRefreshRates.empty()) return true; + + const auto max = std::max_element(mRefreshRates.begin(), mRefreshRates.end()); + const auto min = std::min_element(mRefreshRates.begin(), mRefreshRates.end()); + const auto consistent = + max->refreshRate.getValue() - min->refreshRate.getValue() < MARGIN_CONSISTENT_FPS; + + if (CC_UNLIKELY(sTraceEnabled)) { + if (!mHeuristicTraceTagData.has_value()) { + mHeuristicTraceTagData = makeHeuristicTraceTagData(); + } + + ATRACE_INT(mHeuristicTraceTagData->max.c_str(), max->refreshRate.getIntValue()); + ATRACE_INT(mHeuristicTraceTagData->min.c_str(), min->refreshRate.getIntValue()); + ATRACE_INT(mHeuristicTraceTagData->consistent.c_str(), consistent); + } + + return consistent; } } // namespace android::scheduler + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file |