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

Commit 37c665dc authored by Kevin DuBois's avatar Kevin DuBois Committed by Android (Google) Code Review
Browse files

Merge changes I400fc5e3,I2135f87a

* changes:
  SF: VSyncDispatch: correct vsync prediction drift
  SF: VSyncReactor add event subscription functions
parents 6e72dba8 c94ca839
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ namespace android::scheduler {
class TimeKeeper;
class VSyncTracker;

enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
enum class ScheduleResult { Scheduled, CannotSchedule, Error };
enum class CancelResult { Cancelled, TooLate, Error };

/*
@@ -83,7 +83,6 @@ public:
     * \param [in] earliestVsync   The targeted display time. This will be snapped to the closest
     *                             predicted vsync time after earliestVsync.
     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
     *                             A ScheduleResult::ReScheduled if callback was rescheduled.
     *                             A ScheduleResult::CannotSchedule
     *                             if (workDuration - earliestVsync) is in the past, or
     *                             if a callback was dispatched for the predictedVsync already.
+36 −12
Original line number Diff line number Diff line
@@ -29,8 +29,13 @@ VSyncTracker::~VSyncTracker() = default;
TimeKeeper::~TimeKeeper() = default;

VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
                                                           std::function<void(nsecs_t)> const& cb)
      : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
                                                           std::function<void(nsecs_t)> const& cb,
                                                           nsecs_t minVsyncDistance)
      : mName(name),
        mCallback(cb),
        mWorkDuration(0),
        mEarliestVsync(0),
        mMinVsyncDistance(minVsyncDistance) {}

std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
    return mLastDispatchTime;
@@ -49,18 +54,28 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {

ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
                                                      VSyncTracker& tracker, nsecs_t now) {
    auto const nextVsyncTime =
    auto nextVsyncTime =
            tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
    if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
        return ScheduleResult::CannotSchedule;

    bool const wouldSkipAVsyncTarget =
            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
    if (wouldSkipAVsyncTarget) {
        return ScheduleResult::Scheduled;
    }

    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
    if (alreadyDispatchedForVsync) {
        nextVsyncTime =
                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
    }

    auto const nextWakeupTime = nextVsyncTime - workDuration;
    auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
    mWorkDuration = workDuration;
    mEarliestVsync = earliestVsync;
    mArmedInfo = {nextWakeupTime, nextVsyncTime};
    return result;
    return ScheduleResult::Scheduled;
}

void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
@@ -101,8 +116,12 @@ void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
}

VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
                                                 VSyncTracker& tracker, nsecs_t timerSlack)
      : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
                                                 VSyncTracker& tracker, nsecs_t timerSlack,
                                                 nsecs_t minVsyncDistance)
      : mTimeKeeper(std::move(tk)),
        mTracker(tracker),
        mTimerSlack(timerSlack),
        mMinVsyncDistance(minVsyncDistance) {}

VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
    std::lock_guard<decltype(mMutex)> lk(mMutex);
@@ -187,7 +206,8 @@ VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback
            mCallbacks
                    .emplace(++mCallbackToken,
                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
                                                                            callbackFn))
                                                                            callbackFn,
                                                                            mMinVsyncDistance))
                    .first->first};
}

@@ -277,12 +297,16 @@ VSyncCallbackRegistration::~VSyncCallbackRegistration() {
}

ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
    if (!mValidToken) return ScheduleResult::Error;
    if (!mValidToken) {
        return ScheduleResult::Error;
    }
    return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
}

CancelResult VSyncCallbackRegistration::cancel() {
    if (!mValidToken) return CancelResult::Error;
    if (!mValidToken) {
        return CancelResult::Error;
    }
    return mDispatch.get().cancel(mToken);
}

+12 −2
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ public:
    // Valid transition: disarmed -> armed ( when scheduled )
    // Valid transition: armed -> running -> disarmed ( when timer is called)
    // Valid transition: armed -> disarmed ( when cancelled )
    VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
    VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn,
                                 nsecs_t minVsyncDistance);
    std::string_view name() const;

    // Start: functions that are not threadsafe.
@@ -72,6 +73,7 @@ private:

    nsecs_t mWorkDuration;
    nsecs_t mEarliestVsync;
    nsecs_t const mMinVsyncDistance;

    struct ArmingInfo {
        nsecs_t mActualWakeupTime;
@@ -91,8 +93,15 @@ private:
 */
class VSyncDispatchTimerQueue : public VSyncDispatch {
public:
    // Constructs a VSyncDispatchTimerQueue.
    // \param[in] tk                    A timekeeper.
    // \param[in] tracker               A tracker.
    // \param[in] timerSlack            The threshold at which different similarly timed callbacks
    //                                  should be grouped into one wakeup.
    // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
    //                                  vsyncs are considered the same vsync event.
    explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
                                     nsecs_t timerSlack);
                                     nsecs_t timerSlack, nsecs_t minVsyncDistance);
    ~VSyncDispatchTimerQueue();

    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
@@ -119,6 +128,7 @@ private:
    std::unique_ptr<TimeKeeper> const mTimeKeeper;
    VSyncTracker& mTracker;
    nsecs_t const mTimerSlack;
    nsecs_t const mMinVsyncDistance;

    std::mutex mutable mMutex;
    size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+131 −0
Original line number Diff line number Diff line
@@ -14,7 +14,11 @@
 * limitations under the License.
 */

#undef LOG_TAG
#define LOG_TAG "VSyncReactor"
//#define LOG_NDEBUG 0
#include "VSyncReactor.h"
#include <log/log.h>
#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
@@ -30,6 +34,87 @@ VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDi
        mTracker(std::move(tracker)),
        mPendingLimit(pendingFenceLimit) {}

VSyncReactor::~VSyncReactor() = default;

// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
// for now.
class CallbackRepeater {
public:
    CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
                     nsecs_t period, nsecs_t offset, nsecs_t notBefore)
          : mCallback(cb),
            mRegistration(dispatch,
                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
                          std::string(name)),
            mPeriod(period),
            mOffset(offset),
            mLastCallTime(notBefore) {}

    ~CallbackRepeater() {
        std::lock_guard<std::mutex> lk(mMutex);
        mRegistration.cancel();
    }

    void start(nsecs_t offset) {
        std::lock_guard<std::mutex> lk(mMutex);
        mStopped = false;
        mOffset = offset;

        auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
        LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
                            "Error scheduling callback: rc %X", schedule_result);
    }

    void setPeriod(nsecs_t period) {
        std::lock_guard<std::mutex> lk(mMutex);
        if (period == mPeriod) {
            return;
        }
        mPeriod = period;
    }

    void stop() {
        std::lock_guard<std::mutex> lk(mMutex);
        LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
        mStopped = true;
        mRegistration.cancel();
    }

private:
    void callback(nsecs_t vsynctime) {
        nsecs_t period = 0;
        {
            std::lock_guard<std::mutex> lk(mMutex);
            period = mPeriod;
            mLastCallTime = vsynctime;
        }

        mCallback->onDispSyncEvent(vsynctime - period);

        {
            std::lock_guard<std::mutex> lk(mMutex);
            auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
            LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
                                "Error rescheduling callback: rc %X", schedule_result);
        }
    }

    // DispSync offsets are defined as time after the vsync before presentation.
    // VSyncReactor workloads are defined as time before the intended presentation vsync.
    // Note change in sign between the two defnitions.
    nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }

    DispSync::Callback* const mCallback;

    std::mutex mutable mMutex;
    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
    bool mStopped GUARDED_BY(mMutex) = false;
    nsecs_t mPeriod GUARDED_BY(mMutex);
    nsecs_t mOffset GUARDED_BY(mMutex);
    nsecs_t mLastCallTime GUARDED_BY(mMutex);
};

bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
    if (!fence) {
        return false;
@@ -92,6 +177,9 @@ void VSyncReactor::setPeriod(nsecs_t period) {
    {
        std::lock_guard<std::mutex> lk(mMutex);
        mPeriodChangeInProgress = true;
        for (auto& entry : mCallbacks) {
            entry.second->setPeriod(period);
        }
    }
}

@@ -114,4 +202,47 @@ bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
    return false;
}

status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
                                        DispSync::Callback* callback,
                                        nsecs_t /* lastCallbackTime */) {
    std::lock_guard<std::mutex> lk(mMutex);
    auto it = mCallbacks.find(callback);
    if (it == mCallbacks.end()) {
        // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
        static auto constexpr maxListeners = 3;
        if (mCallbacks.size() >= maxListeners) {
            ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
                  maxListeners, mCallbacks.size());
            return NO_MEMORY;
        }

        auto const period = mTracker->currentPeriod();
        auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
                                                           phase, mClock->now());
        it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
    }

    it->second->start(phase);
    return NO_ERROR;
}

status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
                                           nsecs_t* /* outLastCallback */) {
    std::lock_guard<std::mutex> lk(mMutex);
    auto const it = mCallbacks.find(callback);
    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);

    it->second->stop();
    return NO_ERROR;
}

status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
    std::lock_guard<std::mutex> lk(mMutex);
    auto const it = mCallbacks.find(callback);
    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);

    it->second->start(phase);
    return NO_ERROR;
}

} // namespace android::scheduler
+11 −0
Original line number Diff line number Diff line
@@ -20,19 +20,23 @@
#include <ui/FenceTime.h>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <vector>
#include "DispSync.h"

namespace android::scheduler {

class Clock;
class VSyncDispatch;
class VSyncTracker;
class CallbackRepeater;

// TODO (b/145217110): consider renaming.
class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
public:
    VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
    ~VSyncReactor();

    bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
    void setIgnorePresentFences(bool ignoration);
@@ -48,6 +52,11 @@ public:
    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
    void endResync();

    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
                              nsecs_t lastCallbackTime);
    status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback);
    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase);

private:
    std::unique_ptr<Clock> const mClock;
    std::unique_ptr<VSyncDispatch> const mDispatch;
@@ -58,6 +67,8 @@ private:
    bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
    bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
    std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
            GUARDED_BY(mMutex);
};

} // namespace android::scheduler
Loading