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

Commit 983e568b authored by Ady Abraham's avatar Ady Abraham
Browse files

SurfaceFlinger: keep layer history for inactive layers

The current implementation of layer history discards all the history
if a layer is considered inactive. This leads to an issue in the scenario
that a layer is posting infrequent updates so slow, that by the time we
get the next buffer, the layer is considered inactive, and we bump the
refresh rate to Max, instead of detecting that layer as infrequent.

As a fix, we keep around the layer history even if it is inactive.
To avoid cases where detect animations too late, we clear the history
on touch event.

Bug: 156654519
Test: add a new unit test that simulates the buffer pattern
of a Netflix playback

Change-Id: Ie076891806d74dde814fbbb3b1a73c38e0b2ae34
parent b74fe1dc
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -192,7 +192,7 @@ void LayerHistoryV2::partitionLayers(nsecs_t now) {
            trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
        }

        info->clearHistory();
        info->onLayerInactive(now);
        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
    }

@@ -213,7 +213,7 @@ void LayerHistoryV2::clear() {
    std::lock_guard lock(mLock);

    for (const auto& [layer, info] : activeLayers()) {
        info->clearHistory();
        info->clearHistory(systemTime());
    }
}
} // namespace android::scheduler::impl
+2 −9
Original line number Diff line number Diff line
@@ -57,21 +57,14 @@ bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
}

bool LayerInfoV2::isFrequent(nsecs_t now) const {
    // Find the first valid frame time
    auto it = mFrameTimes.begin();
    for (; it != mFrameTimes.end(); ++it) {
        if (isFrameTimeValid(*it)) {
            break;
        }
    }

    // If we know nothing about this layer we consider it as frequent as it might be the start
    // of an animation.
    if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
        return true;
    }

    // Find the first active frame
    auto it = mFrameTimes.begin();
    for (; it != mFrameTimes.end(); ++it) {
        if (it->queueTime >= getActiveLayerThreshold(now)) {
            break;
+8 −2
Original line number Diff line number Diff line
@@ -83,15 +83,21 @@ public:
    // updated time, the updated time is the present time.
    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }

    void clearHistory() {
    void onLayerInactive(nsecs_t now) {
        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
        // We are not deleting the old frame to keep track of whether we should treat the first
        // buffer as Max as we don't know anything about this layer or Min as this layer is
        // posting infrequent updates.
        mFrameTimeValidSince = std::chrono::steady_clock::now();
        const auto timePoint = std::chrono::nanoseconds(now);
        mFrameTimeValidSince = std::chrono::time_point<std::chrono::steady_clock>(timePoint);
        mLastReportedRefreshRate = 0.0f;
    }

    void clearHistory(nsecs_t now) {
        onLayerInactive(now);
        mFrameTimes.clear();
    }

private:
    // Used to store the layer timestamps
    struct FrameTimeData {
+74 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ protected:
    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
    static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
    static constexpr auto PRESENT_TIME_HISTORY_TIME = LayerInfoV2::HISTORY_TIME;

    static constexpr float LO_FPS = 30.f;
    static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -71,6 +72,9 @@ protected:
    }

    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
    auto createLayer(std::string name) {
        return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
    }

    Hwc2::mock::Display mDisplay;
    RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0)
@@ -541,5 +545,75 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
    EXPECT_EQ(2, frequentLayerCount(time));
}

class LayerHistoryTestV2Parameterized
      : public LayerHistoryTestV2,
        public testing::WithParamInterface<std::chrono::nanoseconds> {};

TEST_P(LayerHistoryTestV2Parameterized, HeuristicLayerWithInfrequentLayer) {
    std::chrono::nanoseconds infrequentUpdateDelta = GetParam();
    auto heuristicLayer = createLayer("HeuristicLayer");

    EXPECT_CALL(*heuristicLayer, isVisible()).WillRepeatedly(Return(true));
    EXPECT_CALL(*heuristicLayer, getFrameRateForLayerTree())
            .WillRepeatedly(Return(Layer::FrameRate()));

    auto infrequentLayer = createLayer("InfrequentLayer");
    EXPECT_CALL(*infrequentLayer, isVisible()).WillRepeatedly(Return(true));
    EXPECT_CALL(*infrequentLayer, getFrameRateForLayerTree())
            .WillRepeatedly(Return(Layer::FrameRate()));

    const nsecs_t startTime = systemTime();

    const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns;
    history().record(heuristicLayer.get(), startTime, startTime);
    history().record(infrequentLayer.get(), startTime, startTime);

    nsecs_t time = startTime;
    nsecs_t lastInfrequentUpdate = startTime;
    const int totalInfrequentLayerUpdates = FREQUENT_LAYER_WINDOW_SIZE * 5;
    int infrequentLayerUpdates = 0;
    while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) {
        time += heuristicUpdateDelta.count();
        history().record(heuristicLayer.get(), time, time);

        if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) {
            ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates,
                  totalInfrequentLayerUpdates);
            lastInfrequentUpdate = time;
            history().record(infrequentLayer.get(), time, time);
            infrequentLayerUpdates++;
        }

        if (time - startTime > PRESENT_TIME_HISTORY_TIME.count()) {
            ASSERT_NE(0, history().summarize(time).size());
            ASSERT_GE(2, history().summarize(time).size());

            bool max = false;
            bool min = false;
            float heuristic = 0;
            for (const auto& layer : history().summarize(time)) {
                if (layer.vote == LayerHistory::LayerVoteType::Heuristic) {
                    heuristic = layer.desiredRefreshRate;
                } else if (layer.vote == LayerHistory::LayerVoteType::Max) {
                    max = true;
                } else if (layer.vote == LayerHistory::LayerVoteType::Min) {
                    min = true;
                }
            }

            if (infrequentLayerUpdates > FREQUENT_LAYER_WINDOW_SIZE) {
                EXPECT_FLOAT_EQ(24.0f, heuristic);
                EXPECT_FALSE(max);
                if (history().summarize(time).size() == 2) {
                    EXPECT_TRUE(min);
                }
            }
        }
    }
}

INSTANTIATE_TEST_CASE_P(LeapYearTests, LayerHistoryTestV2Parameterized,
                        ::testing::Values(1s, 2s, 3s, 4s, 5s));

} // namespace
} // namespace android::scheduler
+3 −2
Original line number Diff line number Diff line
@@ -24,8 +24,9 @@ namespace android::mock {

class MockLayer : public Layer {
public:
    explicit MockLayer(SurfaceFlinger* flinger)
          : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
    MockLayer(SurfaceFlinger* flinger, std::string name)
          : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
    explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}

    MOCK_CONST_METHOD0(getType, const char*());
    MOCK_METHOD0(getFrameSelectionPriority, int32_t());