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

Commit 77b4fb1e authored by Ady Abraham's avatar Ady Abraham
Browse files

SF: reduce latency when switching low to high render rate

Bug: 328086969
Test: presubmit
Change-Id: I5aa1863d7c93d01ffd5c57182b1e085cb52183fa
parent e54ce10b
Loading
Loading
Loading
Loading
+43 −9
Original line number Diff line number Diff line
@@ -296,12 +296,15 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint,
    const auto now = TimePoint::fromNs(mClock->now());
    purgeTimelines(now);

    const auto model = getVSyncPredictionModelLocked();
    const auto threshold = model.slope / 2;
    std::optional<TimePoint> vsyncOpt;
    for (auto& timeline : mTimelines) {
        vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(getVSyncPredictionModelLocked(),
                                                         minFramePeriodLocked(),
        vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(model, minFramePeriodLocked(),
                                                         snapToVsync(timePoint), mMissedVsync,
                                                         lastVsyncOpt);
                                                         lastVsyncOpt ? snapToVsync(*lastVsyncOpt -
                                                                                    threshold)
                                                                      : lastVsyncOpt);
        if (vsyncOpt) {
            break;
        }
@@ -353,9 +356,19 @@ void VSyncPredictor::setRenderRate(Fps renderRate) {
    ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
    ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
    std::lock_guard lock(mMutex);
    const auto prevRenderRate = mRenderRateOpt;
    mRenderRateOpt = renderRate;
    mTimelines.back().freeze(TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
    mTimelines.emplace_back(mIdealPeriod, renderRate);
    const auto renderPeriodDelta =
            prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0;
    if (renderPeriodDelta > renderRate.getPeriodNsecs() &&
        mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs()) {
        mTimelines.clear();
        mLastCommittedVsync = TimePoint::fromNs(0);
    } else {
        mTimelines.back().freeze(
                TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
    }
    mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
    purgeTimelines(TimePoint::fromNs(mClock->now()));
}

@@ -507,7 +520,7 @@ void VSyncPredictor::clearTimestamps() {
    mTimelines.clear();
    mLastCommittedVsync = TimePoint::fromNs(0);
    mIdealPeriod = Period::fromNs(idealPeriod());
    mTimelines.emplace_back(mIdealPeriod, mRenderRateOpt);
    mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
}

bool VSyncPredictor::needsMoreSamples() const {
@@ -535,6 +548,16 @@ void VSyncPredictor::dump(std::string& result) const {
}

void VSyncPredictor::purgeTimelines(android::TimePoint now) {
    const auto kEnoughFramesToBreakPhase = 5;
    if (mRenderRateOpt &&
        mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase <
                mClock->now()) {
        mTimelines.clear();
        mLastCommittedVsync = TimePoint::fromNs(0);
        mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
        return;
    }

    while (mTimelines.size() > 1) {
        const auto validUntilOpt = mTimelines.front().validUntil();
        if (validUntilOpt && *validUntilOpt < now) {
@@ -547,8 +570,17 @@ void VSyncPredictor::purgeTimelines(android::TimePoint now) {
    LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value());
}

VSyncPredictor::VsyncTimeline::VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt)
      : mIdealPeriod(idealPeriod), mRenderRateOpt(renderRateOpt) {}
auto VSyncPredictor::VsyncTimeline::makeVsyncSequence(TimePoint knownVsync)
        -> std::optional<VsyncSequence> {
    if (knownVsync.ns() == 0) return std::nullopt;
    return std::make_optional<VsyncSequence>({knownVsync.ns(), 0});
}

VSyncPredictor::VsyncTimeline::VsyncTimeline(TimePoint knownVsync, Period idealPeriod,
                                             std::optional<Fps> renderRateOpt)
      : mIdealPeriod(idealPeriod),
        mRenderRateOpt(renderRateOpt),
        mLastVsyncSequence(makeVsyncSequence(knownVsync)) {}

void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
    LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
@@ -578,7 +610,9 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime
        if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
            // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
            // first before trying to use it.
            if (mLastVsyncSequence->seq > 0) {
                lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
            }
            const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
            if (vsyncDiff <= minFramePeriod.ns() - threshold) {
                vsyncFixupTime = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
+2 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ private:

    class VsyncTimeline {
    public:
        VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt);
        VsyncTimeline(TimePoint knownVsync, Period idealPeriod, std::optional<Fps> renderRateOpt);
        std::optional<TimePoint> nextAnticipatedVSyncTimeFrom(
                Model model, Period minFramePeriod, nsecs_t vsyncTime, MissedVsync lastMissedVsync,
                std::optional<nsecs_t> lastVsyncOpt = {});
@@ -101,6 +101,7 @@ private:
    private:
        nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
        VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
        std::optional<VsyncSequence> makeVsyncSequence(TimePoint knownVsync);

        const Period mIdealPeriod = Duration::fromNs(0);
        const std::optional<Fps> mRenderRateOpt;
+8 −6
Original line number Diff line number Diff line
@@ -57,6 +57,11 @@ using LayerHierarchy = surfaceflinger::frontend::LayerHierarchy;
using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder;
using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState;

class ZeroClock : public Clock {
public:
    nsecs_t now() const override { return 0; }
};

class SchedulerTest : public testing::Test {
protected:
    class MockEventThreadConnection : public android::EventThreadConnection {
@@ -564,7 +569,7 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) {
                                 hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>(
                                                        frameRate.getPeriodNsecs())}));
    std::shared_ptr<VSyncPredictor> vrrTracker =
            std::make_shared<VSyncPredictor>(std::make_unique<SystemClock>(), kMode, kHistorySize,
            std::make_shared<VSyncPredictor>(std::make_unique<ZeroClock>(), kMode, kHistorySize,
                                             kMinimumSamplesForPrediction,
                                             kOutlierTolerancePercent);
    std::shared_ptr<RefreshRateSelector> vrrSelectorPtr =
@@ -600,15 +605,12 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) {
    vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
    scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);

    // Set 4000 as vsync seq #0
    vrrTracker->nextAnticipatedVSyncTimeFrom(3700);

    EXPECT_EQ(Fps::fromPeriodNsecs(2000),
              scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
                                             TimePoint::fromNs(4000)));
                                             TimePoint::fromNs(4500)));
    EXPECT_EQ(Fps::fromPeriodNsecs(2000),
              scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
                                             TimePoint::fromNs(6000)));
                                             TimePoint::fromNs(6500)));
}

TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
+127 −13
Original line number Diff line number Diff line
@@ -622,13 +622,13 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) {

    tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod));

    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + 3 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 3 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 3 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 6 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 6 * mPeriod));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}

TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
@@ -651,6 +651,119 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}

TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediatley) {
    SET_FLAG_FOR_TEST(flags::vrr_config, 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};

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000));
    vrrTracker.addVsyncTimestamp(0);
    EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
    EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));

    // commit to a vsync in the future
    EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000));
    EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
    EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
    EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));

    EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(3500));
    EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
    EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
    EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
    EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
    EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
    EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
    EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2500));
    EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
    EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
    EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
    EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
    EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
    EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
    EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
    EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000));

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000));
    EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
    EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
    EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
    EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
    EXPECT_EQ(11000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
    EXPECT_EQ(13000, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
    EXPECT_EQ(17000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
    EXPECT_EQ(20000, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000));
}

TEST_F(VSyncPredictorTest, selectsClosestVsyncAfterInactivity) {
    SET_FLAG_FOR_TEST(flags::vrr_config, 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};

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(5000));
    vrrTracker.addVsyncTimestamp(0);
    EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4700));
    EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));

    mClock->setNow(50000);
    EXPECT_EQ(50500, vrrTracker.nextAnticipatedVSyncTimeFrom(50000, 10000));
}

TEST_F(VSyncPredictorTest, returnsCorrectVsyncWhenLastIsNot) {
    SET_FLAG_FOR_TEST(flags::vrr_config, 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};

    vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000));
    vrrTracker.addVsyncTimestamp(0);
    EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1234, 1234));
}

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

@@ -743,17 +856,18 @@ TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(8000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));

    tracker.setRenderRate(Fps::fromPeriodNsecs(3000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(8000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(11001), Eq(14000));
    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(12001), Eq(14000));