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

Commit b1471a0b authored by Arthur Hung's avatar Arthur Hung Committed by Android (Google) Code Review
Browse files

Merge "SF: Suppress frame rate when small area updating" into main

parents 79f18efa c70bee29
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -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;
}

@@ -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);
    }();

@@ -3297,6 +3333,7 @@ bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
    mDrawingState.surfaceDamageRegion = surfaceDamage;
    mDrawingState.modified = true;
    setTransactionFlags(eTransactionNeeded);
    setIsSmallDirty();
    return true;
}

@@ -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_)
+12 −0
Original line number Diff line number Diff line
@@ -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.
@@ -903,6 +911,7 @@ public:
                .transform = getTransform(),
                .setFrameRateVote = getFrameRateForLayerTree(),
                .frameRateSelectionPriority = getFrameRateSelectionPriority(),
                .isSmallDirty = mSmallDirty,
        };
    };
    bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; }
@@ -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;
+7 −0
Original line number Diff line number Diff line
@@ -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
+5 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ struct LayerProps;
class LayerHistory {
public:
    using LayerVoteType = RefreshRateSelector::LayerVoteType;
    static constexpr std::chrono::nanoseconds kMaxPeriodForHistory = 1s;

    LayerHistory();
    ~LayerHistory();
@@ -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>;
+28 −2
Original line number Diff line number Diff line
@@ -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();
@@ -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;
        }
@@ -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
@@ -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);
@@ -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) {
@@ -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;
    }
@@ -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