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

Commit e54ce10b authored by Ady Abraham's avatar Ady Abraham
Browse files

SF: fix vsync shift repeatedly when HWC miss a frame

Bug: 328085852
Change-Id: I17df8ae1fcf5e21662e582e795d00a3d800640f3
Test: presubmit
parent 20024aaf
Loading
Loading
Loading
Loading
+21 −24
Original line number Diff line number Diff line
@@ -45,28 +45,6 @@ using base::StringAppendF;

static auto constexpr kMaxPercent = 100u;

namespace {
nsecs_t getVsyncFixup(VSyncPredictor::Model model, Period minFramePeriod, nsecs_t vsyncTime,
                      std::optional<nsecs_t> lastVsyncOpt) {
    const auto threshold = model.slope / 2;

    if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
        const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
        if (vsyncDiff >= threshold && vsyncDiff <= minFramePeriod.ns() - threshold) {
            const auto vsyncFixup = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
            ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from prev. "
                                  "adjust by %.2f",
                                  static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
                                  static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
                                  static_cast<float>(vsyncFixup) / 1e6f);
            return vsyncFixup;
        }
    }

    return 0;
}
} // namespace

VSyncPredictor::~VSyncPredictor() = default;

VSyncPredictor::VSyncPredictor(std::unique_ptr<Clock> clock, ftl::NonNull<DisplayModePtr> modePtr,
@@ -585,16 +563,33 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime
        std::optional<nsecs_t> lastVsyncOpt) {
    ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");

    nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
    const auto threshold = model.slope / 2;
    const auto lastFrameMissed =
            lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
    nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
    nsecs_t vsyncFixupTime = 0;
    if (FlagManager::getInstance().vrr_config() && lastFrameMissed) {
        // If the last frame missed is the last vsync, we already shifted the timeline. Depends on
        // whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a different
        // fixup. There is no need to to shift the vsync timeline again.
        vsyncTime += missedVsync.fixup.ns();
        ATRACE_FORMAT_INSTANT("lastFrameMissed");
    } else {
        vsyncFixupTime = getVsyncFixup(model, minFramePeriod, vsyncTime, lastVsyncOpt);
        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.
            lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
            const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
            if (vsyncDiff <= minFramePeriod.ns() - threshold) {
                vsyncFixupTime = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
                ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from "
                                      "prev. "
                                      "adjust by %.2f",
                                      static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
                                      static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
                                      static_cast<float>(vsyncFixupTime) / 1e6f);
            }
        }
        vsyncTime += vsyncFixupTime;
    }

@@ -605,6 +600,8 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime
        return std::nullopt;
    }

    // If we needed a fixup, it means that we changed the render rate and the chosen vsync would
    // cross minFramePeriod. In that case we need to shift the entire vsync timeline.
    if (vsyncFixupTime > 0) {
        shiftVsyncSequence(Duration::fromNs(vsyncFixupTime));
    }
+5 −4
Original line number Diff line number Diff line
@@ -591,6 +591,7 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) {
                                             TimePoint::fromNs(2000)));

    // Not crossing the min frame period
    vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
    EXPECT_EQ(Fps::fromPeriodNsecs(1000),
              scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
                                             TimePoint::fromNs(2500)));
@@ -599,15 +600,15 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) {
    vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
    scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);

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

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

TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
+44 −0
Original line number Diff line number Diff line
@@ -688,6 +688,50 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) {
    EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}

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

    const int32_t kGroup = 0;
    const auto kResolution = ui::Size(1920, 1080);
    const auto refreshRate = 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), refreshRate, kGroup,
                                                      kResolution, DEFAULT_DISPLAY_ID)
                                     .setVrrConfig(std::move(vrrConfig))
                                     .build());

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

    vrrTracker.setRenderRate(minFrameRate);
    vrrTracker.addVsyncTimestamp(0);
    EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));

    EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 3000));

    EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
    EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
    vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0));

    EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
    EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3700));
    vrrTracker.onFrameMissed(TimePoint::fromNs(4000));

    EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
    vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500));

    EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
    EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
    vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500));

    EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500));
    EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500));
    vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500));
}

TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
    tracker.addVsyncTimestamp(1000);