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

Commit 486d5b74 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "SF: dispatch add testing surface for VSyncDispatch"

parents c5ce61f2 e4f27a8d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -165,7 +165,7 @@ filegroup {
        "Scheduler/PhaseOffsets.cpp",
        "Scheduler/Scheduler.cpp",
        "Scheduler/SchedulerUtils.cpp",
        "Scheduler/VSyncDispatch.cpp",
        "Scheduler/VSyncDispatchTimerQueue.cpp",
        "Scheduler/VSyncModulator.cpp",
        "StartPropertySetThread.cpp",
        "SurfaceFlinger.cpp",
+9 −102
Original line number Diff line number Diff line
@@ -16,14 +16,9 @@

#pragma once

#include <android-base/thread_annotations.h>
#include <utils/Timers.h>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>

#include "StrongTyping.h"

@@ -34,68 +29,6 @@ class VSyncTracker;
enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
enum class CancelResult { Cancelled, TooLate, Error };

namespace impl {

// VSyncDispatchEntry is a helper class representing internal state for each entry in VSyncDispatch
// hoisted to public for unit testing.
class VSyncDispatchEntry {
public:
    // This is the state of the entry. There are 3 states, armed, running, disarmed.
    // Valid transition: disarmed -> armed ( when scheduled )
    // Valid transition: armed -> running -> disarmed ( when timer is called)
    // Valid transition: armed -> disarmed ( when cancelled )
    VSyncDispatchEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
    std::string_view name() const;

    // Start: functions that are not threadsafe.
    // Return the last vsync time this callback was invoked.
    std::optional<nsecs_t> lastExecutedVsyncTarget() const;

    // This moves the state from disarmed->armed and will calculate the wakeupTime.
    nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
                     nsecs_t now);
    // This will update armed entries with the latest vsync information. Entry remains armed.
    void update(VSyncTracker& tracker, nsecs_t now);

    // This will return empty if not armed, or the next calculated wakeup time if armed.
    // It will not update the wakeupTime.
    std::optional<nsecs_t> wakeupTime() const;

    // This moves state from armed->disarmed.
    void disarm();

    // This moves the state from armed->running.
    // Store the timestamp that this was intended for as the last called timestamp.
    nsecs_t executing();
    // End: functions that are not threadsafe.

    // Invoke the callback with the timestamp, moving the state from running->disarmed.
    void callback(nsecs_t timestamp);
    // Block calling thread while the callback is executing.
    void ensureNotRunning();

private:
    void arm(VSyncTracker& tracker, nsecs_t now);
    std::string const mName;
    std::function<void(nsecs_t)> const mCallback;

    nsecs_t mWorkDuration;
    nsecs_t mEarliestVsync;

    struct ArmingInfo {
        nsecs_t mActualWakeupTime;
        nsecs_t mActualVsyncTime;
    };
    std::optional<ArmingInfo> mArmedInfo;
    std::optional<nsecs_t> mLastDispatchTime;

    std::mutex mRunningMutex;
    std::condition_variable mCv;
    bool mRunning GUARDED_BY(mRunningMutex) = false;
};

} // namespace impl

/*
 * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
 */
@@ -103,15 +36,7 @@ class VSyncDispatch {
public:
    using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;

    /* creates a VsyncDispatch.
     * \param [in]  a timekeeper object for dispatching events.
     * \param [in]  a tracker object that is monitoring expected vsync events.
     * \param [in]  a tunable in nanoseconds that indicates when events that fall close together
     *              should be dispatched in one timer wakeup.
     */
    explicit VSyncDispatch(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
                           nsecs_t timerSlack);
    ~VSyncDispatch();
    virtual ~VSyncDispatch();

    /*
     * Registers a callback that will be called at designated points on the vsync timeline.
@@ -126,8 +51,8 @@ public:
     *                          invocation of callbackFn.
     *
     */
    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
                                   std::string callbackName);
    virtual CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
                                           std::string callbackName) = 0;

    /*
     * Unregisters a callback.
@@ -135,7 +60,7 @@ public:
     * \param [in] token        The callback to unregister.
     *
     */
    void unregisterCallback(CallbackToken token);
    virtual void unregisterCallback(CallbackToken token) = 0;

    /*
     * Schedules the registered callback to be dispatched.
@@ -164,7 +89,8 @@ public:
     *                             if a callback was dispatched for the predictedVsync already.
     *                             A ScheduleResult::Error if there was another error.
     */
    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync);
    virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
                                    nsecs_t earliestVsync) = 0;

    /* Cancels a scheduled callback, if possible.
     *
@@ -173,31 +99,12 @@ public:
     *                      A CancelResult::Cancelled if the callback was successfully cancelled.
     *                      A CancelResult::Error if there was an pre-condition violation.
     */
    CancelResult cancel(CallbackToken token);
    virtual CancelResult cancel(CallbackToken token) = 0;

private:
protected:
    VSyncDispatch() = default;
    VSyncDispatch(VSyncDispatch const&) = delete;
    VSyncDispatch& operator=(VSyncDispatch const&) = delete;

    using CallbackMap = std::unordered_map<size_t, std::shared_ptr<impl::VSyncDispatchEntry>>;

    void timerCallback();
    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
            REQUIRES(mMutex);
    void cancelTimer() REQUIRES(mMutex);

    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
    std::unique_ptr<TimeKeeper> const mTimeKeeper;
    VSyncTracker& mTracker;
    nsecs_t const mTimerSlack;

    std::mutex mutable mMutex;
    size_t mCallbackToken GUARDED_BY(mMutex) = 0;

    CallbackMap mCallbacks GUARDED_BY(mMutex);
    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
};

/*
+35 −32
Original line number Diff line number Diff line
@@ -19,34 +19,35 @@
#include <vector>

#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncDispatchTimerQueue.h"
#include "VSyncTracker.h"

namespace android::scheduler {

VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
TimeKeeper::~TimeKeeper() = default;

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

std::optional<nsecs_t> impl::VSyncDispatchEntry::lastExecutedVsyncTarget() const {
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
    return mLastDispatchTime;
}

std::string_view impl::VSyncDispatchEntry::name() const {
std::string_view VSyncDispatchTimerQueueEntry::name() const {
    return mName;
}

std::optional<nsecs_t> impl::VSyncDispatchEntry::wakeupTime() const {
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
    if (!mArmedInfo) {
        return {};
    }
    return {mArmedInfo->mActualWakeupTime};
}

nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
                                               VSyncTracker& tracker, nsecs_t now) {
    mWorkDuration = workDuration;
    mEarliestVsync = earliestVsync;
@@ -54,30 +55,30 @@ nsecs_t impl::VSyncDispatchEntry::schedule(nsecs_t workDuration, nsecs_t earlies
    return mArmedInfo->mActualWakeupTime;
}

void impl::VSyncDispatchEntry::update(VSyncTracker& tracker, nsecs_t now) {
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
    if (!mArmedInfo) {
        return;
    }
    arm(tracker, now);
}

void impl::VSyncDispatchEntry::arm(VSyncTracker& tracker, nsecs_t now) {
void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
    auto const nextVsyncTime =
            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
}

void impl::VSyncDispatchEntry::disarm() {
void VSyncDispatchTimerQueueEntry::disarm() {
    mArmedInfo.reset();
}

nsecs_t impl::VSyncDispatchEntry::executing() {
nsecs_t VSyncDispatchTimerQueueEntry::executing() {
    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
    disarm();
    return *mLastDispatchTime;
}

void impl::VSyncDispatchEntry::callback(nsecs_t t) {
void VSyncDispatchTimerQueueEntry::callback(nsecs_t t) {
    {
        std::lock_guard<std::mutex> lk(mRunningMutex);
        mRunning = true;
@@ -90,36 +91,37 @@ void impl::VSyncDispatchEntry::callback(nsecs_t t) {
    mCv.notify_all();
}

void impl::VSyncDispatchEntry::ensureNotRunning() {
void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
    std::unique_lock<std::mutex> lk(mRunningMutex);
    mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
}

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

VSyncDispatch::~VSyncDispatch() {
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
    std::lock_guard<decltype(mMutex)> lk(mMutex);
    cancelTimer();
}

void VSyncDispatch::cancelTimer() {
void VSyncDispatchTimerQueue::cancelTimer() {
    mIntendedWakeupTime = kInvalidTime;
    mTimeKeeper->alarmCancel();
}

void VSyncDispatch::setTimer(nsecs_t targetTime, nsecs_t now) {
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
    mIntendedWakeupTime = targetTime;
    mTimeKeeper->alarmIn(std::bind(&VSyncDispatch::timerCallback, this), targetTime - now);
    mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
                         targetTime - now);
}

void VSyncDispatch::rearmTimer(nsecs_t now) {
void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
    rearmTimerSkippingUpdateFor(now, mCallbacks.end());
}

void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now,
                                                CallbackMap::iterator const& skipUpdateIt) {
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
    std::optional<nsecs_t> min;
    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
        auto& callback = it->second;
@@ -143,9 +145,9 @@ void VSyncDispatch::rearmTimerSkippingUpdateFor(nsecs_t now,
    }
}

void VSyncDispatch::timerCallback() {
void VSyncDispatchTimerQueue::timerCallback() {
    struct Invocation {
        std::shared_ptr<impl::VSyncDispatchEntry> callback;
        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
        nsecs_t timestamp;
    };
    std::vector<Invocation> invocations;
@@ -174,18 +176,19 @@ void VSyncDispatch::timerCallback() {
    }
}

VSyncDispatch::CallbackToken VSyncDispatch::registerCallback(
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
        std::function<void(nsecs_t)> const& callbackFn, std::string callbackName) {
    std::lock_guard<decltype(mMutex)> lk(mMutex);
    return CallbackToken{
            mCallbacks
                    .emplace(++mCallbackToken,
                             std::make_shared<impl::VSyncDispatchEntry>(callbackName, callbackFn))
                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
                                                                            callbackFn))
                    .first->first};
}

void VSyncDispatch::unregisterCallback(CallbackToken token) {
    std::shared_ptr<impl::VSyncDispatchEntry> entry = nullptr;
void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
    std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
    {
        std::lock_guard<decltype(mMutex)> lk(mMutex);
        auto it = mCallbacks.find(token);
@@ -200,7 +203,7 @@ void VSyncDispatch::unregisterCallback(CallbackToken token) {
    }
}

ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration,
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
                                                 nsecs_t earliestVsync) {
    auto result = ScheduleResult::Error;
    {
@@ -228,7 +231,7 @@ ScheduleResult VSyncDispatch::schedule(CallbackToken token, nsecs_t workDuration
    return result;
}

CancelResult VSyncDispatch::cancel(CallbackToken token) {
CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
    std::lock_guard<decltype(mMutex)> lk(mMutex);

    auto it = mCallbacks.find(token);
+130 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <android-base/thread_annotations.h>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>

#include "VSyncDispatch.h"

namespace android::scheduler {

// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
// VSyncDispatchTimerQueue hoisted to public for unit testing.
class VSyncDispatchTimerQueueEntry {
public:
    // This is the state of the entry. There are 3 states, armed, running, disarmed.
    // 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);
    std::string_view name() const;

    // Start: functions that are not threadsafe.
    // Return the last vsync time this callback was invoked.
    std::optional<nsecs_t> lastExecutedVsyncTarget() const;

    // This moves the state from disarmed->armed and will calculate the wakeupTime.
    nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
                     nsecs_t now);
    // This will update armed entries with the latest vsync information. Entry remains armed.
    void update(VSyncTracker& tracker, nsecs_t now);

    // This will return empty if not armed, or the next calculated wakeup time if armed.
    // It will not update the wakeupTime.
    std::optional<nsecs_t> wakeupTime() const;

    // This moves state from armed->disarmed.
    void disarm();

    // This moves the state from armed->running.
    // Store the timestamp that this was intended for as the last called timestamp.
    nsecs_t executing();
    // End: functions that are not threadsafe.

    // Invoke the callback with the timestamp, moving the state from running->disarmed.
    void callback(nsecs_t timestamp);
    // Block calling thread while the callback is executing.
    void ensureNotRunning();

private:
    void arm(VSyncTracker& tracker, nsecs_t now);
    std::string const mName;
    std::function<void(nsecs_t)> const mCallback;

    nsecs_t mWorkDuration;
    nsecs_t mEarliestVsync;

    struct ArmingInfo {
        nsecs_t mActualWakeupTime;
        nsecs_t mActualVsyncTime;
    };
    std::optional<ArmingInfo> mArmedInfo;
    std::optional<nsecs_t> mLastDispatchTime;

    std::mutex mRunningMutex;
    std::condition_variable mCv;
    bool mRunning GUARDED_BY(mRunningMutex) = false;
};

/*
 * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
 * using a single timer queue.
 */
class VSyncDispatchTimerQueue : public VSyncDispatch {
public:
    explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
                                     nsecs_t timerSlack);
    ~VSyncDispatchTimerQueue();

    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
                                   std::string callbackName) final;
    void unregisterCallback(CallbackToken token) final;
    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
    CancelResult cancel(CallbackToken token) final;

private:
    VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
    VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;

    using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;

    void timerCallback();
    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
            REQUIRES(mMutex);
    void cancelTimer() REQUIRES(mMutex);

    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
    std::unique_ptr<TimeKeeper> const mTimeKeeper;
    VSyncTracker& mTracker;
    nsecs_t const mTimerSlack;

    std::mutex mutable mMutex;
    size_t mCallbackToken GUARDED_BY(mMutex) = 0;

    CallbackMap mCallbacks GUARDED_BY(mMutex);
    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
};

} // namespace android::scheduler
+1 −1
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ cc_test {
        "FrameTracerTest.cpp",
        "TransactionApplicationTest.cpp",
        "StrongTypingTest.cpp",
        "VSyncDispatchTest.cpp",
        "VSyncDispatchTimerQueueTest.cpp",
        "mock/DisplayHardware/MockComposer.cpp",
        "mock/DisplayHardware/MockDisplay.cpp",
        "mock/DisplayHardware/MockPowerAdvisor.cpp",
Loading