Loading services/surfaceflinger/Layer.cpp +56 −0 Original line number Diff line number Diff line Loading @@ -3212,6 +3212,14 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, } mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; // If the layer had been updated a TextureView, this would make sure the present time could be // same to TextureView update when it's a small dirty, and get the correct heuristic rate. if (mFlinger->mScheduler->supportSmallDirtyDetection()) { if (mDrawingState.useVsyncIdForRefreshRateSelection) { mUsedVsyncIdForRefreshRateSelection = true; } } return true; } Loading @@ -3234,10 +3242,38 @@ void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerPro mDrawingState.latchedVsyncId); if (prediction.has_value()) { ATRACE_FORMAT_INSTANT("predictedPresentTime"); mMaxTimeForUseVsyncId = prediction->presentTime + scheduler::LayerHistory::kMaxPeriodForHistory.count(); return prediction->presentTime; } } if (!mFlinger->mScheduler->supportSmallDirtyDetection()) { return static_cast<nsecs_t>(0); } // If the layer is not an application and didn't set an explicit rate or desiredPresentTime, // return "0" to tell the layer history that it will use the max refresh rate without // calculating the adaptive rate. if (mWindowType != WindowInfo::Type::APPLICATION && mWindowType != WindowInfo::Type::BASE_APPLICATION) { return static_cast<nsecs_t>(0); } // Return the valid present time only when the layer potentially updated a TextureView so // LayerHistory could heuristically calculate the rate if the UI is continually updating. if (mUsedVsyncIdForRefreshRateSelection) { const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken( mDrawingState.latchedVsyncId); if (prediction.has_value()) { if (mMaxTimeForUseVsyncId >= prediction->presentTime) { return prediction->presentTime; } mUsedVsyncIdForRefreshRateSelection = false; } } return static_cast<nsecs_t>(0); }(); Loading Loading @@ -3297,6 +3333,7 @@ bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) { mDrawingState.surfaceDamageRegion = surfaceDamage; mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); setIsSmallDirty(); return true; } Loading Loading @@ -4335,6 +4372,25 @@ void Layer::updateLastLatchTime(nsecs_t latchTime) { mLastLatchTime = latchTime; } void Layer::setIsSmallDirty() { if (!mFlinger->mScheduler->supportSmallDirtyDetection()) { return; } if (mWindowType != WindowInfo::Type::APPLICATION && mWindowType != WindowInfo::Type::BASE_APPLICATION) { return; } Rect bounds = mDrawingState.surfaceDamageRegion.getBounds(); if (!bounds.isValid()) { return; } // If the damage region is a small dirty, this could give the hint for the layer history that // it could suppress the heuristic rate when calculating. mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(bounds.getWidth() * bounds.getHeight()); } } // namespace android #if defined(__gl_h_) Loading services/surfaceflinger/Layer.h +12 −0 Original line number Diff line number Diff line Loading @@ -841,6 +841,14 @@ public: mutable bool contentDirty{false}; Region surfaceDamageRegion; // True when the surfaceDamageRegion is recognized as a small area update. bool mSmallDirty{false}; // Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating. nsecs_t mMaxTimeForUseVsyncId = 0; // True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating // buffer. bool mUsedVsyncIdForRefreshRateSelection{false}; // Layer serial number. This gives layers an explicit ordering, so we // have a stable sort order when their layer stack and Z-order are // the same. Loading Loading @@ -903,6 +911,7 @@ public: .transform = getTransform(), .setFrameRateVote = getFrameRateForLayerTree(), .frameRateSelectionPriority = getFrameRateSelectionPriority(), .isSmallDirty = mSmallDirty, }; }; bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; } Loading @@ -917,6 +926,9 @@ public: // Exposed so SurfaceFlinger can assert that it's held const sp<SurfaceFlinger> mFlinger; // Check if the damage region is a small dirty. void setIsSmallDirty(); protected: // For unit tests friend class TestableSurfaceFlinger; Loading services/surfaceflinger/Scheduler/LayerHistory.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -306,4 +306,11 @@ auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> { return {LayerStatus::NotFound, nullptr}; } bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea) const { const float ratio = (float)dirtyArea / mDisplayArea; const bool isSmallDirty = ratio <= kSmallDirtyArea; ATRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio); return isSmallDirty; } } // namespace android::scheduler services/surfaceflinger/Scheduler/LayerHistory.h +5 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ struct LayerProps; class LayerHistory { public: using LayerVoteType = RefreshRateSelector::LayerVoteType; static constexpr std::chrono::nanoseconds kMaxPeriodForHistory = 1s; LayerHistory(); ~LayerHistory(); Loading Loading @@ -84,10 +85,14 @@ public: // return the frames per second of the layer with the given sequence id. float getLayerFramerate(nsecs_t now, int32_t id) const; bool isSmallDirtyArea(uint32_t dirtyArea) const; private: friend class LayerHistoryTest; friend class TestableScheduler; static constexpr float kSmallDirtyArea = 0.07f; using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>; // keyed by id as returned from Layer::getSequence() using LayerInfos = std::unordered_map<int32_t, LayerPair>; Loading services/surfaceflinger/Scheduler/LayerInfo.cpp +28 −2 Original line number Diff line number Diff line Loading @@ -65,7 +65,8 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUp case LayerUpdateType::Buffer: FrameTimeData frameTime = {.presentTime = lastPresentTime, .queueTime = mLastUpdatedTime, .pendingModeChange = pendingModeChange}; .pendingModeChange = pendingModeChange, .isSmallDirty = props.isSmallDirty}; mFrameTimes.push_back(frameTime); if (mFrameTimes.size() > HISTORY_SIZE) { mFrameTimes.pop_front(); Loading Loading @@ -101,11 +102,15 @@ LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const { // classification. bool isFrequent = true; bool isInfrequent = true; int32_t smallDirtyCount = 0; const auto n = mFrameTimes.size() - 1; for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) { if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime < kMaxPeriodForFrequentLayerNs.count()) { isInfrequent = false; if (mFrameTimes[n - i].presentTime == 0 && mFrameTimes[n - i].isSmallDirty) { smallDirtyCount++; } } else { isFrequent = false; } Loading @@ -115,7 +120,8 @@ LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const { // If the layer was previously inconclusive, we clear // the history as indeterminate layers changed to frequent, // and we should not look at the stale data. return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true}; return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true, /* isSmallDirty */ smallDirtyCount >= kNumSmallDirtyThreshold}; } // If we can't determine whether the layer is frequent or not, we return Loading Loading @@ -204,6 +210,7 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { nsecs_t totalDeltas = 0; int numDeltas = 0; int32_t smallDirtyCount = 0; auto prevFrame = mFrameTimes.begin(); for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) { const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame); Loading @@ -212,6 +219,13 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { continue; } // If this is a small area update, we don't want to consider it for calculating the average // frame time. Instead, we let the bigger frame updates to drive the calculation. if (it->isSmallDirty && currDelta < kMinPeriodBetweenSmallDirtyFrames) { smallDirtyCount++; continue; } prevFrame = it; if (currDelta > kMaxPeriodBetweenFrames) { Loading @@ -223,6 +237,10 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { numDeltas++; } if (smallDirtyCount > 0) { ATRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount); } if (numDeltas == 0) { return std::nullopt; } Loading Loading @@ -313,6 +331,14 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec clearHistory(now); } // Return no vote if the latest frames are small dirty. if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) { ATRACE_FORMAT_INSTANT("NoVote (small dirty)"); ALOGV("%s is small dirty", mName.c_str()); votes.push_back({LayerHistory::LayerVoteType::NoVote, Fps()}); return votes; } auto refreshRate = calculateRefreshRateIfPossible(selector, now); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); Loading Loading
services/surfaceflinger/Layer.cpp +56 −0 Original line number Diff line number Diff line Loading @@ -3212,6 +3212,14 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, } mDrawingState.releaseBufferEndpoint = bufferData.releaseBufferEndpoint; // If the layer had been updated a TextureView, this would make sure the present time could be // same to TextureView update when it's a small dirty, and get the correct heuristic rate. if (mFlinger->mScheduler->supportSmallDirtyDetection()) { if (mDrawingState.useVsyncIdForRefreshRateSelection) { mUsedVsyncIdForRefreshRateSelection = true; } } return true; } Loading @@ -3234,10 +3242,38 @@ void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerPro mDrawingState.latchedVsyncId); if (prediction.has_value()) { ATRACE_FORMAT_INSTANT("predictedPresentTime"); mMaxTimeForUseVsyncId = prediction->presentTime + scheduler::LayerHistory::kMaxPeriodForHistory.count(); return prediction->presentTime; } } if (!mFlinger->mScheduler->supportSmallDirtyDetection()) { return static_cast<nsecs_t>(0); } // If the layer is not an application and didn't set an explicit rate or desiredPresentTime, // return "0" to tell the layer history that it will use the max refresh rate without // calculating the adaptive rate. if (mWindowType != WindowInfo::Type::APPLICATION && mWindowType != WindowInfo::Type::BASE_APPLICATION) { return static_cast<nsecs_t>(0); } // Return the valid present time only when the layer potentially updated a TextureView so // LayerHistory could heuristically calculate the rate if the UI is continually updating. if (mUsedVsyncIdForRefreshRateSelection) { const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken( mDrawingState.latchedVsyncId); if (prediction.has_value()) { if (mMaxTimeForUseVsyncId >= prediction->presentTime) { return prediction->presentTime; } mUsedVsyncIdForRefreshRateSelection = false; } } return static_cast<nsecs_t>(0); }(); Loading Loading @@ -3297,6 +3333,7 @@ bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) { mDrawingState.surfaceDamageRegion = surfaceDamage; mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); setIsSmallDirty(); return true; } Loading Loading @@ -4335,6 +4372,25 @@ void Layer::updateLastLatchTime(nsecs_t latchTime) { mLastLatchTime = latchTime; } void Layer::setIsSmallDirty() { if (!mFlinger->mScheduler->supportSmallDirtyDetection()) { return; } if (mWindowType != WindowInfo::Type::APPLICATION && mWindowType != WindowInfo::Type::BASE_APPLICATION) { return; } Rect bounds = mDrawingState.surfaceDamageRegion.getBounds(); if (!bounds.isValid()) { return; } // If the damage region is a small dirty, this could give the hint for the layer history that // it could suppress the heuristic rate when calculating. mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(bounds.getWidth() * bounds.getHeight()); } } // namespace android #if defined(__gl_h_) Loading
services/surfaceflinger/Layer.h +12 −0 Original line number Diff line number Diff line Loading @@ -841,6 +841,14 @@ public: mutable bool contentDirty{false}; Region surfaceDamageRegion; // True when the surfaceDamageRegion is recognized as a small area update. bool mSmallDirty{false}; // Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating. nsecs_t mMaxTimeForUseVsyncId = 0; // True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating // buffer. bool mUsedVsyncIdForRefreshRateSelection{false}; // Layer serial number. This gives layers an explicit ordering, so we // have a stable sort order when their layer stack and Z-order are // the same. Loading Loading @@ -903,6 +911,7 @@ public: .transform = getTransform(), .setFrameRateVote = getFrameRateForLayerTree(), .frameRateSelectionPriority = getFrameRateSelectionPriority(), .isSmallDirty = mSmallDirty, }; }; bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; } Loading @@ -917,6 +926,9 @@ public: // Exposed so SurfaceFlinger can assert that it's held const sp<SurfaceFlinger> mFlinger; // Check if the damage region is a small dirty. void setIsSmallDirty(); protected: // For unit tests friend class TestableSurfaceFlinger; Loading
services/surfaceflinger/Scheduler/LayerHistory.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -306,4 +306,11 @@ auto LayerHistory::findLayer(int32_t id) -> std::pair<LayerStatus, LayerPair*> { return {LayerStatus::NotFound, nullptr}; } bool LayerHistory::isSmallDirtyArea(uint32_t dirtyArea) const { const float ratio = (float)dirtyArea / mDisplayArea; const bool isSmallDirty = ratio <= kSmallDirtyArea; ATRACE_FORMAT_INSTANT("small dirty=%s, ratio=%.3f", isSmallDirty ? "true" : "false", ratio); return isSmallDirty; } } // namespace android::scheduler
services/surfaceflinger/Scheduler/LayerHistory.h +5 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ struct LayerProps; class LayerHistory { public: using LayerVoteType = RefreshRateSelector::LayerVoteType; static constexpr std::chrono::nanoseconds kMaxPeriodForHistory = 1s; LayerHistory(); ~LayerHistory(); Loading Loading @@ -84,10 +85,14 @@ public: // return the frames per second of the layer with the given sequence id. float getLayerFramerate(nsecs_t now, int32_t id) const; bool isSmallDirtyArea(uint32_t dirtyArea) const; private: friend class LayerHistoryTest; friend class TestableScheduler; static constexpr float kSmallDirtyArea = 0.07f; using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>; // keyed by id as returned from Layer::getSequence() using LayerInfos = std::unordered_map<int32_t, LayerPair>; Loading
services/surfaceflinger/Scheduler/LayerInfo.cpp +28 −2 Original line number Diff line number Diff line Loading @@ -65,7 +65,8 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUp case LayerUpdateType::Buffer: FrameTimeData frameTime = {.presentTime = lastPresentTime, .queueTime = mLastUpdatedTime, .pendingModeChange = pendingModeChange}; .pendingModeChange = pendingModeChange, .isSmallDirty = props.isSmallDirty}; mFrameTimes.push_back(frameTime); if (mFrameTimes.size() > HISTORY_SIZE) { mFrameTimes.pop_front(); Loading Loading @@ -101,11 +102,15 @@ LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const { // classification. bool isFrequent = true; bool isInfrequent = true; int32_t smallDirtyCount = 0; const auto n = mFrameTimes.size() - 1; for (size_t i = 0; i < kFrequentLayerWindowSize - 1; i++) { if (mFrameTimes[n - i].queueTime - mFrameTimes[n - i - 1].queueTime < kMaxPeriodForFrequentLayerNs.count()) { isInfrequent = false; if (mFrameTimes[n - i].presentTime == 0 && mFrameTimes[n - i].isSmallDirty) { smallDirtyCount++; } } else { isFrequent = false; } Loading @@ -115,7 +120,8 @@ LayerInfo::Frequent LayerInfo::isFrequent(nsecs_t now) const { // If the layer was previously inconclusive, we clear // the history as indeterminate layers changed to frequent, // and we should not look at the stale data. return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true}; return {isFrequent, isFrequent && !mIsFrequencyConclusive, /* isConclusive */ true, /* isSmallDirty */ smallDirtyCount >= kNumSmallDirtyThreshold}; } // If we can't determine whether the layer is frequent or not, we return Loading Loading @@ -204,6 +210,7 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { nsecs_t totalDeltas = 0; int numDeltas = 0; int32_t smallDirtyCount = 0; auto prevFrame = mFrameTimes.begin(); for (auto it = mFrameTimes.begin() + 1; it != mFrameTimes.end(); ++it) { const auto currDelta = getFrameTime(*it) - getFrameTime(*prevFrame); Loading @@ -212,6 +219,13 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { continue; } // If this is a small area update, we don't want to consider it for calculating the average // frame time. Instead, we let the bigger frame updates to drive the calculation. if (it->isSmallDirty && currDelta < kMinPeriodBetweenSmallDirtyFrames) { smallDirtyCount++; continue; } prevFrame = it; if (currDelta > kMaxPeriodBetweenFrames) { Loading @@ -223,6 +237,10 @@ std::optional<nsecs_t> LayerInfo::calculateAverageFrameTime() const { numDeltas++; } if (smallDirtyCount > 0) { ATRACE_FORMAT_INSTANT("small dirty = %" PRIu32, smallDirtyCount); } if (numDeltas == 0) { return std::nullopt; } Loading Loading @@ -313,6 +331,14 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec clearHistory(now); } // Return no vote if the latest frames are small dirty. if (frequent.isSmallDirty && !mLastRefreshRate.reported.isValid()) { ATRACE_FORMAT_INSTANT("NoVote (small dirty)"); ALOGV("%s is small dirty", mName.c_str()); votes.push_back({LayerHistory::LayerVoteType::NoVote, Fps()}); return votes; } auto refreshRate = calculateRefreshRateIfPossible(selector, now); if (refreshRate.has_value()) { ALOGV("%s calculated refresh rate: %s", mName.c_str(), to_string(*refreshRate).c_str()); Loading