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

Commit 548f4497 authored by ramindani's avatar ramindani
Browse files

SF: Update vsync timeline validUntil to be exact vsync time.

Introduce Timeline::isWithin function that puts sourced vsync time
source into Unique, Shared and Outside categories.

BUG: 343603085
Test: atest VSyncPredictorTest
Flag: com.android.graphics.surfaceflinger.flags.vrr_bugfix_24q4
Change-Id: I99094e64c8403455158a6e794bd4d2ce32429f93
parent 33f4e0e6
Loading
Loading
Loading
Loading
+27 −8
Original line number Diff line number Diff line
@@ -360,7 +360,11 @@ bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, Fps frameRate) {
    purgeTimelines(now);

    for (auto& timeline : mTimelines) {
        if (timeline.validUntil() && timeline.validUntil()->ns() > vsync) {
        const bool isVsyncValid = FlagManager::getInstance().vrr_bugfix_24q4()
                ? timeline.isWithin(TimePoint::fromNs(vsync)) ==
                        VsyncTimeline::VsyncOnTimeline::Unique
                : timeline.validUntil() && timeline.validUntil()->ns() > vsync;
        if (isVsyncValid) {
            return timeline.isVSyncInPhase(model, vsync, frameRate);
        }
    }
@@ -394,10 +398,16 @@ void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) {
        mTimelines.clear();
        mLastCommittedVsync = TimePoint::fromNs(0);

    } else {
        if (FlagManager::getInstance().vrr_bugfix_24q4()) {
            // We need to freeze the timeline at the committed vsync so that we don't
            // overshoot the deadline.
            mTimelines.back().freeze(mLastCommittedVsync);
        } else {
            mTimelines.back().freeze(
                    TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
        }
    }
    mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
    purgeTimelines(TimePoint::fromNs(mClock->now()));
}
@@ -611,7 +621,10 @@ void VSyncPredictor::purgeTimelines(android::TimePoint now) {

    while (mTimelines.size() > 1) {
        const auto validUntilOpt = mTimelines.front().validUntil();
        if (validUntilOpt && *validUntilOpt < now) {
        const bool isTimelineOutDated = FlagManager::getInstance().vrr_bugfix_24q4()
                ? mTimelines.front().isWithin(now) == VsyncTimeline::VsyncOnTimeline::Outside
                : validUntilOpt && *validUntilOpt < now;
        if (isTimelineOutDated) {
            mTimelines.pop_front();
        } else {
            break;
@@ -660,9 +673,12 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime
            vsyncTime += missedVsync.fixup.ns();
            ATRACE_FORMAT_INSTANT("lastFrameMissed");
        } else if (mightBackpressure && lastVsyncOpt) {
            // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
            // first before trying to use it.
            if (!FlagManager::getInstance().vrr_bugfix_24q4()) {
                // lastVsyncOpt does not need to be corrected with the new rate, and
                // it should be used as is to avoid skipping a frame when changing rates are
                // aligned at vsync time.
                lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
            }
            const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
            if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) {
                // avoid a duplicate vsync
@@ -681,7 +697,10 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime
    }

    ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
    if (mValidUntil && vsyncTime > mValidUntil->ns()) {
    const bool isVsyncInvalid = FlagManager::getInstance().vrr_bugfix_24q4()
            ? isWithin(TimePoint::fromNs(vsyncTime)) == VsyncOnTimeline::Outside
            : mValidUntil && vsyncTime > mValidUntil->ns();
    if (isVsyncInvalid) {
        ATRACE_FORMAT_INSTANT("no longer valid for vsync in %.2f",
                              static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f);
        return std::nullopt;
+18 −0
Original line number Diff line number Diff line
@@ -106,6 +106,24 @@ private:
        void shiftVsyncSequence(Duration phase);
        void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }

        enum class VsyncOnTimeline {
            Unique,  // Within timeline, not shared with next timeline.
            Shared,  // Within timeline, shared with next timeline.
            Outside, // Outside of the timeline.
        };
        VsyncOnTimeline isWithin(TimePoint vsync) {
            const auto threshold = mIdealPeriod.ns() / 2;
            if (!mValidUntil || vsync.ns() < mValidUntil->ns() - threshold) {
                // if mValidUntil is absent then timeline is not frozen and
                // vsync should be unique to that timeline.
                return VsyncOnTimeline::Unique;
            }
            if (vsync.ns() > mValidUntil->ns() + threshold) {
                return VsyncOnTimeline::Outside;
            }
            return VsyncOnTimeline::Shared;
        }

    private:
        nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
        VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
+30 −0
Original line number Diff line number Diff line
@@ -673,6 +673,36 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}

TEST_F(VSyncPredictorTest, setRenderRateWhenRenderRateGoesDown) {
    SET_FLAG_FOR_TEST(flags::vrr_config, true);
    SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true);

    const int32_t kGroup = 0;
    const auto kResolution = ui::Size(1920, 1080);
    const auto vsyncRate = Fps::fromPeriodNsecs(500);
    const auto minFrameRate = Fps::fromPeriodNsecs(1000);
    hal::VrrConfig vrrConfig;
    vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
    const ftl::NonNull<DisplayModePtr> kMode =
            ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
                                                      kResolution, DEFAULT_DISPLAY_ID)
                                     .setVrrConfig(std::move(vrrConfig))
                                     .build());

    VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
                              kMinimumSamplesForPrediction, kOutlierTolerancePercent};

    Fps frameRate = Fps::fromPeriodNsecs(1000);
    vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false);
    vrrTracker.addVsyncTimestamp(0);
    EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
    EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));

    frameRate = Fps::fromPeriodNsecs(3000);
    vrrTracker.setRenderRate(frameRate, /*applyImmediately*/ false);
    EXPECT_TRUE(vrrTracker.isVSyncInPhase(2000, frameRate));
}

TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) {
    SET_FLAG_FOR_TEST(flags::vrr_config, true);