Loading services/surfaceflinger/Fps.h 0 → 100644 +112 −0 Original line number Diff line number Diff line /* * Copyright (C) 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 <cmath> #include <ostream> #include <string> #include <android-base/stringprintf.h> #include <utils/Timers.h> namespace android { // Value which represents "frames per second". This class is a wrapper around // float, providing some useful utilities, such as comparisons with tolerance // and converting between period duruation and frequency. class Fps { public: static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); } Fps() = default; explicit constexpr Fps(float fps) : fps(fps), period(fps == 0.0f ? 0 : static_cast<nsecs_t>(1e9f / fps)) {} constexpr float getValue() const { return fps; } constexpr nsecs_t getPeriodNsecs() const { return period; } bool equalsWithMargin(const Fps& other) const { return std::abs(fps - other.fps) < kMargin; } // DO NOT use for std::sort. Instead use comparesLess(). bool lessThanWithMargin(const Fps& other) const { return fps + kMargin < other.fps; } bool greaterThanWithMargin(const Fps& other) const { return fps > other.fps + kMargin; } bool lessThanOrEqualWithMargin(const Fps& other) const { return !greaterThanWithMargin(other); } bool greaterThanOrEqualWithMargin(const Fps& other) const { return !lessThanWithMargin(other); } bool isValid() const { return fps > 0.0f; } int getIntValue() const { return static_cast<int>(std::round(fps)); } // Use this comparator for sorting. Using a comparator with margins can // cause std::sort to crash. inline static bool comparesLess(const Fps& left, const Fps& right) { return left.fps < right.fps; } // Compares two FPS with margin. // Transitivity is not guaranteed, i.e. a==b and b==c doesn't imply a==c. // DO NOT use with hash maps. Instead use EqualsInBuckets. struct EqualsWithMargin { bool operator()(const Fps& left, const Fps& right) const { return left.equalsWithMargin(right); } }; // Equals comparator which can be used with hash maps. // It's guaranteed that if two elements are equal, then their hashes are equal. struct EqualsInBuckets { bool operator()(const Fps& left, const Fps& right) const { return left.getBucket() == right.getBucket(); } }; inline friend std::string to_string(const Fps& fps) { return base::StringPrintf("%.2ffps", fps.fps); } inline friend std::ostream& operator<<(std::ostream& os, const Fps& fps) { return os << to_string(fps); } private: friend std::hash<android::Fps>; constexpr Fps(float fps, nsecs_t period) : fps(fps), period(period) {} float getBucket() const { return std::round(fps / kMargin); } static constexpr float kMargin = 0.001f; float fps = 0; nsecs_t period = 0; }; static_assert(std::is_trivially_copyable_v<Fps>); } // namespace android namespace std { template <> struct hash<android::Fps> { std::size_t operator()(const android::Fps& fps) const { return std::hash<float>()(fps.getBucket()); } }; } // namespace std No newline at end of file services/surfaceflinger/Layer.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -1425,7 +1425,8 @@ void Layer::updateTreeHasFrameRateVote() { // First traverse the tree and count how many layers has votes int layersWithVote = 0; traverseTree([&layersWithVote](Layer* layer) { const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 && const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate.isValid() && layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default; const auto layerVotedWithNoVote = layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote; Loading Loading @@ -1486,14 +1487,14 @@ void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, ns Layer::FrameRate Layer::getFrameRateForLayerTree() const { const auto frameRate = getDrawingState().frameRate; if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) { if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) { return frameRate; } // This layer doesn't have a frame rate. If one of its ancestors or successors // have a vote, return a NoVote for ancestors/successors to set the vote if (getDrawingState().treeHasFrameRateVote) { return {0, FrameRateCompatibility::NoVote}; return {Fps(0.0f), FrameRateCompatibility::NoVote}; } return frameRate; Loading Loading @@ -1687,9 +1688,9 @@ void Layer::miniDump(std::string& result, const DisplayDevice& display) const { const FloatRect& crop = outputLayerState.sourceCrop; StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right, crop.bottom); if (layerState.frameRate.rate != 0 || if (layerState.frameRate.rate.isValid() || layerState.frameRate.type != FrameRateCompatibility::Default) { StringAppendF(&result, "% 6.2ffps %15s seamless=%s", layerState.frameRate.rate, StringAppendF(&result, "%s %15s seamless=%s", to_string(layerState.frameRate.rate).c_str(), frameRateCompatibilityString(layerState.frameRate.type).c_str(), toString(layerState.frameRate.seamlessness).c_str()); } else { Loading services/surfaceflinger/Layer.h +7 −5 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ #include "ClientCache.h" #include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/HWComposer.h" #include "Fps.h" #include "FrameTracker.h" #include "LayerVector.h" #include "MonitoredProducer.h" Loading Loading @@ -155,7 +156,7 @@ public: struct FrameRate { using Seamlessness = scheduler::Seamlessness; float rate; Fps rate; FrameRateCompatibility type; Seamlessness seamlessness; Loading @@ -163,11 +164,12 @@ public: : rate(0), type(FrameRateCompatibility::Default), seamlessness(Seamlessness::Default) {} FrameRate(float rate, FrameRateCompatibility type, bool shouldBeSeamless = true) FrameRate(Fps rate, FrameRateCompatibility type, bool shouldBeSeamless = true) : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {} bool operator==(const FrameRate& other) const { return rate == other.rate && type == other.type && seamlessness == other.seamlessness; return rate.equalsWithMargin(other.rate) && type == other.type && seamlessness == other.seamlessness; } bool operator!=(const FrameRate& other) const { return !(*this == other); } Loading @@ -177,8 +179,8 @@ public: static FrameRateCompatibility convertCompatibility(int8_t compatibility); private: static Seamlessness getSeamlessness(float rate, bool shouldBeSeamless) { if (rate == 0.0f) { static Seamlessness getSeamlessness(Fps rate, bool shouldBeSeamless) { if (!rate.isValid()) { // Refresh rate of 0 is a special value which should reset the vote to // its default value. return Seamlessness::Default; Loading services/surfaceflinger/RefreshRateOverlay.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -190,7 +190,7 @@ bool RefreshRateOverlay::createLayer() { Mutex::Autolock _l(mFlinger.mStateLock); mLayer = mClient->getLayerUser(mIBinder); mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote)); mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote)); // setting Layer's Z requires resorting layersSortedByZ ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer); Loading @@ -205,7 +205,7 @@ bool RefreshRateOverlay::createLayer() { void RefreshRateOverlay::primeCache() { auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates(); if (allRefreshRates.size() == 1) { auto fps = allRefreshRates.begin()->second->getFps(); int fps = allRefreshRates.begin()->second->getFps().getIntValue(); half4 color = {LOW_FPS_COLOR, ALPHA}; mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner)); return; Loading @@ -214,7 +214,7 @@ void RefreshRateOverlay::primeCache() { std::vector<uint32_t> supportedFps; supportedFps.reserve(allRefreshRates.size()); for (auto& [ignored, refreshRate] : allRefreshRates) { supportedFps.push_back(refreshRate->getFps()); supportedFps.push_back(refreshRate->getFps().getIntValue()); } std::sort(supportedFps.begin(), supportedFps.end()); Loading @@ -240,7 +240,7 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { } void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) { mCurrentFps = refreshRate.getFps(); mCurrentFps = refreshRate.getFps().getIntValue(); auto buffer = mBufferCache[*mCurrentFps][mFrame]; mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {}, mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */)); Loading services/surfaceflinger/Scheduler/LayerHistory.cpp +5 −6 Original line number Diff line number Diff line Loading @@ -41,7 +41,7 @@ namespace { bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) { // Layers with an explicit vote are always kept active if (layer.getFrameRateForLayerTree().rate > 0) { if (layer.getFrameRateForLayerTree().rate.isValid()) { return true; } Loading Loading @@ -86,9 +86,8 @@ LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs) LayerHistory::~LayerHistory() = default; void LayerHistory::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate); void LayerHistory::registerLayer(Layer* layer, Fps highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = highRefreshRate.getPeriodNsecs(); auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type); std::lock_guard lock(mLock); mLayerInfos.emplace_back(layer, std::move(info)); Loading Loading @@ -148,7 +147,7 @@ LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused}); if (CC_UNLIKELY(mTraceEnabled)) { trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps))); trace(layer, *info, vote.type, vote.fps.getIntValue()); } } Loading Loading @@ -177,7 +176,7 @@ void LayerHistory::partitionLayers(nsecs_t now) { } }(); if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) { if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) { const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote; info->setLayerVote({type, frameRate.rate, frameRate.seamlessness}); } else { Loading Loading
services/surfaceflinger/Fps.h 0 → 100644 +112 −0 Original line number Diff line number Diff line /* * Copyright (C) 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 <cmath> #include <ostream> #include <string> #include <android-base/stringprintf.h> #include <utils/Timers.h> namespace android { // Value which represents "frames per second". This class is a wrapper around // float, providing some useful utilities, such as comparisons with tolerance // and converting between period duruation and frequency. class Fps { public: static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); } Fps() = default; explicit constexpr Fps(float fps) : fps(fps), period(fps == 0.0f ? 0 : static_cast<nsecs_t>(1e9f / fps)) {} constexpr float getValue() const { return fps; } constexpr nsecs_t getPeriodNsecs() const { return period; } bool equalsWithMargin(const Fps& other) const { return std::abs(fps - other.fps) < kMargin; } // DO NOT use for std::sort. Instead use comparesLess(). bool lessThanWithMargin(const Fps& other) const { return fps + kMargin < other.fps; } bool greaterThanWithMargin(const Fps& other) const { return fps > other.fps + kMargin; } bool lessThanOrEqualWithMargin(const Fps& other) const { return !greaterThanWithMargin(other); } bool greaterThanOrEqualWithMargin(const Fps& other) const { return !lessThanWithMargin(other); } bool isValid() const { return fps > 0.0f; } int getIntValue() const { return static_cast<int>(std::round(fps)); } // Use this comparator for sorting. Using a comparator with margins can // cause std::sort to crash. inline static bool comparesLess(const Fps& left, const Fps& right) { return left.fps < right.fps; } // Compares two FPS with margin. // Transitivity is not guaranteed, i.e. a==b and b==c doesn't imply a==c. // DO NOT use with hash maps. Instead use EqualsInBuckets. struct EqualsWithMargin { bool operator()(const Fps& left, const Fps& right) const { return left.equalsWithMargin(right); } }; // Equals comparator which can be used with hash maps. // It's guaranteed that if two elements are equal, then their hashes are equal. struct EqualsInBuckets { bool operator()(const Fps& left, const Fps& right) const { return left.getBucket() == right.getBucket(); } }; inline friend std::string to_string(const Fps& fps) { return base::StringPrintf("%.2ffps", fps.fps); } inline friend std::ostream& operator<<(std::ostream& os, const Fps& fps) { return os << to_string(fps); } private: friend std::hash<android::Fps>; constexpr Fps(float fps, nsecs_t period) : fps(fps), period(period) {} float getBucket() const { return std::round(fps / kMargin); } static constexpr float kMargin = 0.001f; float fps = 0; nsecs_t period = 0; }; static_assert(std::is_trivially_copyable_v<Fps>); } // namespace android namespace std { template <> struct hash<android::Fps> { std::size_t operator()(const android::Fps& fps) const { return std::hash<float>()(fps.getBucket()); } }; } // namespace std No newline at end of file
services/surfaceflinger/Layer.cpp +6 −5 Original line number Diff line number Diff line Loading @@ -1425,7 +1425,8 @@ void Layer::updateTreeHasFrameRateVote() { // First traverse the tree and count how many layers has votes int layersWithVote = 0; traverseTree([&layersWithVote](Layer* layer) { const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate > 0 && const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate.isValid() && layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default; const auto layerVotedWithNoVote = layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote; Loading Loading @@ -1486,14 +1487,14 @@ void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, ns Layer::FrameRate Layer::getFrameRateForLayerTree() const { const auto frameRate = getDrawingState().frameRate; if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) { if (frameRate.rate.isValid() || frameRate.type == FrameRateCompatibility::NoVote) { return frameRate; } // This layer doesn't have a frame rate. If one of its ancestors or successors // have a vote, return a NoVote for ancestors/successors to set the vote if (getDrawingState().treeHasFrameRateVote) { return {0, FrameRateCompatibility::NoVote}; return {Fps(0.0f), FrameRateCompatibility::NoVote}; } return frameRate; Loading Loading @@ -1687,9 +1688,9 @@ void Layer::miniDump(std::string& result, const DisplayDevice& display) const { const FloatRect& crop = outputLayerState.sourceCrop; StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f | ", crop.left, crop.top, crop.right, crop.bottom); if (layerState.frameRate.rate != 0 || if (layerState.frameRate.rate.isValid() || layerState.frameRate.type != FrameRateCompatibility::Default) { StringAppendF(&result, "% 6.2ffps %15s seamless=%s", layerState.frameRate.rate, StringAppendF(&result, "%s %15s seamless=%s", to_string(layerState.frameRate.rate).c_str(), frameRateCompatibilityString(layerState.frameRate.type).c_str(), toString(layerState.frameRate.seamlessness).c_str()); } else { Loading
services/surfaceflinger/Layer.h +7 −5 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ #include "ClientCache.h" #include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/HWComposer.h" #include "Fps.h" #include "FrameTracker.h" #include "LayerVector.h" #include "MonitoredProducer.h" Loading Loading @@ -155,7 +156,7 @@ public: struct FrameRate { using Seamlessness = scheduler::Seamlessness; float rate; Fps rate; FrameRateCompatibility type; Seamlessness seamlessness; Loading @@ -163,11 +164,12 @@ public: : rate(0), type(FrameRateCompatibility::Default), seamlessness(Seamlessness::Default) {} FrameRate(float rate, FrameRateCompatibility type, bool shouldBeSeamless = true) FrameRate(Fps rate, FrameRateCompatibility type, bool shouldBeSeamless = true) : rate(rate), type(type), seamlessness(getSeamlessness(rate, shouldBeSeamless)) {} bool operator==(const FrameRate& other) const { return rate == other.rate && type == other.type && seamlessness == other.seamlessness; return rate.equalsWithMargin(other.rate) && type == other.type && seamlessness == other.seamlessness; } bool operator!=(const FrameRate& other) const { return !(*this == other); } Loading @@ -177,8 +179,8 @@ public: static FrameRateCompatibility convertCompatibility(int8_t compatibility); private: static Seamlessness getSeamlessness(float rate, bool shouldBeSeamless) { if (rate == 0.0f) { static Seamlessness getSeamlessness(Fps rate, bool shouldBeSeamless) { if (!rate.isValid()) { // Refresh rate of 0 is a special value which should reset the vote to // its default value. return Seamlessness::Default; Loading
services/surfaceflinger/RefreshRateOverlay.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -190,7 +190,7 @@ bool RefreshRateOverlay::createLayer() { Mutex::Autolock _l(mFlinger.mStateLock); mLayer = mClient->getLayerUser(mIBinder); mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote)); mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote)); // setting Layer's Z requires resorting layersSortedByZ ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer); Loading @@ -205,7 +205,7 @@ bool RefreshRateOverlay::createLayer() { void RefreshRateOverlay::primeCache() { auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates(); if (allRefreshRates.size() == 1) { auto fps = allRefreshRates.begin()->second->getFps(); int fps = allRefreshRates.begin()->second->getFps().getIntValue(); half4 color = {LOW_FPS_COLOR, ALPHA}; mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner)); return; Loading @@ -214,7 +214,7 @@ void RefreshRateOverlay::primeCache() { std::vector<uint32_t> supportedFps; supportedFps.reserve(allRefreshRates.size()); for (auto& [ignored, refreshRate] : allRefreshRates) { supportedFps.push_back(refreshRate->getFps()); supportedFps.push_back(refreshRate->getFps().getIntValue()); } std::sort(supportedFps.begin(), supportedFps.end()); Loading @@ -240,7 +240,7 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { } void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) { mCurrentFps = refreshRate.getFps(); mCurrentFps = refreshRate.getFps().getIntValue(); auto buffer = mBufferCache[*mCurrentFps][mFrame]; mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {}, mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */)); Loading
services/surfaceflinger/Scheduler/LayerHistory.cpp +5 −6 Original line number Diff line number Diff line Loading @@ -41,7 +41,7 @@ namespace { bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) { // Layers with an explicit vote are always kept active if (layer.getFrameRateForLayerTree().rate > 0) { if (layer.getFrameRateForLayerTree().rate.isValid()) { return true; } Loading Loading @@ -86,9 +86,8 @@ LayerHistory::LayerHistory(const RefreshRateConfigs& refreshRateConfigs) LayerHistory::~LayerHistory() = default; void LayerHistory::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate); void LayerHistory::registerLayer(Layer* layer, Fps highRefreshRate, LayerVoteType type) { const nsecs_t highRefreshRatePeriod = highRefreshRate.getPeriodNsecs(); auto info = std::make_unique<LayerInfo>(layer->getName(), highRefreshRatePeriod, type); std::lock_guard lock(mLock); mLayerInfos.emplace_back(layer, std::move(info)); Loading Loading @@ -148,7 +147,7 @@ LayerHistory::Summary LayerHistory::summarize(nsecs_t now) { {strong->getName(), vote.type, vote.fps, vote.seamlessness, weight, layerFocused}); if (CC_UNLIKELY(mTraceEnabled)) { trace(layer, *info, vote.type, static_cast<int>(std::round(vote.fps))); trace(layer, *info, vote.type, vote.fps.getIntValue()); } } Loading Loading @@ -177,7 +176,7 @@ void LayerHistory::partitionLayers(nsecs_t now) { } }(); if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) { if (frameRate.rate.isValid() || voteType == LayerVoteType::NoVote) { const auto type = layer->isVisible() ? voteType : LayerVoteType::NoVote; info->setLayerVote({type, frameRate.rate, frameRate.seamlessness}); } else { Loading