Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6bc812d7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Toolkit touch boost to per-uid in sf scheduler" into main

parents b8591fb3 7e796bc1
Loading
Loading
Loading
Loading
+70 −50
Original line number Diff line number Diff line
@@ -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);
+85 −0
Original line number Diff line number Diff line
@@ -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;