diff options
| author | 2024-11-05 10:58:48 -0800 | |
|---|---|---|
| committer | 2024-11-05 17:38:57 -0800 | |
| commit | 7e796bc1f827bf02adffdb4df06833e64915757e (patch) | |
| tree | ca467d002a2c8fec0ba07e04a4248ea96bc7a299 | |
| parent | a343244ebdde94cad82dd7e835cd295833645258 (diff) | |
Toolkit touch boost to per-uid in sf scheduler
Fix where the toolkit touchboost ("HighHint" category) is ignored by
ExplicitDefault frameRate# vote **only** if it has the same UID (app).
- App that setFrameRate(#, default compatibility) still disables touch
boost
- Fixes cases such as game with setFrameRate(#, default) but HighHint
touch on other app e.g. notification shade. The HighHint touch on
notification shade should boost, while the game has frame rate
override.
Bug: 372531483
Flag: EXEMPT bugfix
Test: Manual with game 30 Default and notification shade
Test: atest libsurfaceflinger_unittest
Change-Id: I4c85abe1aad053c3692ffad3e571d8a74ee02227
| -rw-r--r-- | services/surfaceflinger/Scheduler/RefreshRateSelector.cpp | 120 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp | 85 |
2 files changed, 155 insertions, 50 deletions
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index eca8df27d6..68e34d2bfd 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -489,6 +489,20 @@ auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement return mGetRankedFrameRatesCache->result; } +using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; +using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; + +PerUidLayerRequirements groupLayersByUid( + const std::vector<RefreshRateSelector::LayerRequirement>& layers) { + PerUidLayerRequirements layersByUid; + for (const auto& layer : layers) { + const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; + auto& layersWithSameUid = it->second; + layersWithSameUid.push_back(&layer); + } + return layersByUid; +} + auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, GlobalSignals signals, Fps pacesetterFps) const -> RankedFrameRates { @@ -525,6 +539,43 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, GlobalSignals{.powerOnImminent = true}}; } + // A method for UI Toolkit to send the touch signal via "HighHint" category vote, + // which will touch boost when there are no ExplicitDefault layer votes on the app. + // At most one app can have the "HighHint" touch boost vote at a time. + // This accounts for cases such as games that use `setFrameRate` + // with Default compatibility to limit the frame rate and disabling touch boost. + bool isAppTouchBoost = false; + const auto layersByUid = groupLayersByUid(layers); + for (const auto& [uid, layersWithSameUid] : layersByUid) { + bool hasHighHint = false; + bool hasExplicitDefault = false; + for (const auto& layer : layersWithSameUid) { + switch (layer->vote) { + case LayerVoteType::ExplicitDefault: + hasExplicitDefault = true; + break; + case LayerVoteType::ExplicitCategory: + if (layer->frameRateCategory == FrameRateCategory::HighHint) { + hasHighHint = true; + } + break; + default: + // No action + break; + } + if (hasHighHint && hasExplicitDefault) { + break; + } + } + + if (hasHighHint && !hasExplicitDefault) { + // Focused app has touch signal (HighHint) and no frame rate ExplicitDefault votes + // (which prevents touch boost due to games use case). + isAppTouchBoost = true; + break; + } + } + int noVoteLayers = 0; // Layers that prefer the same mode ("no-op"). int noPreferenceLayers = 0; @@ -535,7 +586,6 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi int explicitExact = 0; int explicitGteLayers = 0; int explicitCategoryVoteLayers = 0; - int interactiveLayers = 0; int seamedFocusedLayers = 0; int categorySmoothSwitchOnlyLayers = 0; @@ -563,11 +613,9 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi explicitGteLayers++; break; case LayerVoteType::ExplicitCategory: - if (layer.frameRateCategory == FrameRateCategory::HighHint) { - // HighHint does not count as an explicit signal from an app. It may be - // be a touch signal. - interactiveLayers++; - } else { + // HighHint does not count as an explicit signal from an app. It is a touch signal + // sent from UI Toolkit. + if (layer.frameRateCategory != FrameRateCategory::HighHint) { explicitCategoryVoteLayers++; } if (layer.frameRateCategory == FrameRateCategory::NoPreference) { @@ -882,14 +930,11 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size(); }; - // A method for UI Toolkit to send the touch signal via "HighHint" category vote, - // which will touch boost when there are no ExplicitDefault layer votes. This is an - // incomplete solution but accounts for cases such as games that use `setFrameRate` with default + // This accounts for cases such as games that use `setFrameRate` with Default // compatibility to limit the frame rate, which should not have touch boost. - const bool hasInteraction = signals.touch || interactiveLayers > 0; - - if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() && - isTouchBoostForCategory()) { + const bool isLateGlobalTouchBoost = signals.touch && explicitDefaultVoteLayers == 0; + const bool isLateTouchBoost = isLateGlobalTouchBoost || isAppTouchBoost; + if (isLateTouchBoost && isTouchBoostForExplicitExact() && isTouchBoostForCategory()) { const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; @@ -917,42 +962,6 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, kNoSignals}; } -using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; -using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; - -PerUidLayerRequirements groupLayersByUid( - const std::vector<RefreshRateSelector::LayerRequirement>& layers) { - PerUidLayerRequirements layersByUid; - for (const auto& layer : layers) { - const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; - auto& layersWithSameUid = it->second; - layersWithSameUid.push_back(&layer); - } - - // Remove uids that can't have a frame rate override - for (auto it = layersByUid.begin(); it != layersByUid.end();) { - const auto& layersWithSameUid = it->second; - bool skipUid = false; - for (const auto& layer : layersWithSameUid) { - using LayerVoteType = RefreshRateSelector::LayerVoteType; - - if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) { - ALOGV("%s: %s skips uid=%d due to the vote", __func__, - formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid); - skipUid = true; - break; - } - } - if (skipUid) { - it = layersByUid.erase(it); - } else { - ++it; - } - } - - return layersByUid; -} - auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers, Fps displayRefreshRate, GlobalSignals globalSignals) const @@ -997,6 +1006,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme bool hasExplicitExactOrMultiple = false; bool hasExplicitDefault = false; bool hasHighHint = false; + bool hasSkipOverrideLayer = false; for (const auto& layer : layersWithSameUid) { switch (layer->vote) { case LayerVoteType::ExplicitExactOrMultiple: @@ -1010,15 +1020,25 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme hasHighHint = true; } break; + case LayerVoteType::Max: + case LayerVoteType::Heuristic: + hasSkipOverrideLayer = true; + break; default: // No action break; } - if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) { + if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint && + hasSkipOverrideLayer) { break; } } + if (hasSkipOverrideLayer) { + ALOGV("%s: Skipping due to vote(s): uid=%d", __func__, uid); + continue; + } + // Layers with ExplicitExactOrMultiple expect touch boost if (globalSignals.touch && hasExplicitExactOrMultiple) { ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 29e1c21878..b5be8dba96 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -2240,6 +2240,46 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_Touch EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_touchBoost_twoUids_arr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::Normal; + lr1.name = "ExplicitCategory Normal"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); + // No global touch boost, for example a game that uses setFrameRate(30, default compatibility). + // However see 60 due to Normal vote. + EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz, + actualRankedFrameRates.ranking.front().frameRateMode); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // Gets touch boost because the touched (HighHint) app is different from the 30 Default app. + actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); + EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz, + actualRankedFrameRates.ranking.front().frameRateMode); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) { SET_FLAG_FOR_TEST(flags::vrr_config, false); @@ -3825,6 +3865,51 @@ TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { EXPECT_TRUE(frameRateOverrides.empty()); } +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids_arr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::Normal; + lr1.name = "ExplicitCategory Normal"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // No global touch boost, for example a game that uses setFrameRate(30, default compatibility). + // The `displayFrameRate` is 60. + // However 30 Default app still gets frame rate override. + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 60_Hz, {}); + EXPECT_EQ(2u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // Gets touch boost because the touched (HighHint) app is different from the 30 Default app. + // The `displayFrameRate` is 120 (late touch boost). + // However 30 Default app still gets frame rate override. + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); +} + TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) { if (GetParam() == Config::FrameRateOverride::Disabled) { return; |