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

Commit 9c53ee71 authored by Ady Abraham's avatar Ady Abraham
Browse files

SurfaceFlinger: use duration instead of offset

This change is using the duration given to sf/app without
converting them back to the legacy offset. This allows us to get
the expected vsync associated with a wakeup event.

This change also required some refactoring:
 - Move the periodic callback interface out of DispSync and in
   to DispSyncSource
 - Translate legacy offsets to duration in case that duration
   is not specified in the build files
 - Add support to VSD to expose the deadline timestamp of when
   a frame needs to be ready by

Bug: 162888874
Test: SF unit tests
Test: examine systraces

Change-Id: I87a53cc7dea931d3c195eab6842e003ca4516885
parent 60bc00c1
Loading
Loading
Loading
Loading
+28 −24
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ enum class samplingStep {

constexpr auto timeForRegionSampling = 5000000ns;
constexpr auto maxRegionSamplingSkips = 10;
constexpr auto defaultRegionSamplingOffset = -3ms;
constexpr auto defaultRegionSamplingWorkDuration = 3ms;
constexpr auto defaultRegionSamplingPeriod = 100ms;
constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
// TODO: (b/127403193) duration to string conversion could probably be constexpr
@@ -73,9 +73,9 @@ inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
    char value[PROPERTY_VALUE_MAX] = {};

    property_get("debug.sf.region_sampling_offset_ns", value,
                 toNsString(defaultRegionSamplingOffset).c_str());
    int const samplingOffsetNsRaw = atoi(value);
    property_get("debug.sf.region_sampling_duration_ns", value,
                 toNsString(defaultRegionSamplingWorkDuration).c_str());
    int const samplingDurationNsRaw = atoi(value);

    property_get("debug.sf.region_sampling_period_ns", value,
                 toNsString(defaultRegionSamplingPeriod).c_str());
@@ -87,22 +87,26 @@ RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {

    if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
        ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
        mSamplingOffset = defaultRegionSamplingOffset;
        mSamplingDuration = defaultRegionSamplingWorkDuration;
        mSamplingPeriod = defaultRegionSamplingPeriod;
        mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
    } else {
        mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
        mSamplingDuration = std::chrono::nanoseconds(samplingDurationNsRaw);
        mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
        mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
    }
}

struct SamplingOffsetCallback : DispSync::Callback {
struct SamplingOffsetCallback : VSyncSource::Callback {
    SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
                           std::chrono::nanoseconds targetSamplingOffset)
                           std::chrono::nanoseconds targetSamplingWorkDuration)
          : mRegionSamplingThread(samplingThread),
            mScheduler(scheduler),
            mTargetSamplingOffset(targetSamplingOffset) {}
            mTargetSamplingWorkDuration(targetSamplingWorkDuration),
            mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns,
                                                             0ns,
                                                             /*traceVsync=*/false)) {
        mVSyncSource->setCallback(this);
    }

    ~SamplingOffsetCallback() { stopVsyncListener(); }

@@ -114,8 +118,7 @@ struct SamplingOffsetCallback : DispSync::Callback {
        if (mVsyncListening) return;

        mPhaseIntervalSetting = Phase::ZERO;
        mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
                                                         mLastCallbackTime);
        mVSyncSource->setVSyncEnabled(true);
        mVsyncListening = true;
    }

@@ -128,23 +131,24 @@ private:
    void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
        if (!mVsyncListening) return;

        mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
        mVSyncSource->setVSyncEnabled(false);
        mVsyncListening = false;
    }

    void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final {
    void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/,
                      nsecs_t /*deadlineTimestamp*/) final {
        std::unique_lock<decltype(mMutex)> lock(mMutex);

        if (mPhaseIntervalSetting == Phase::ZERO) {
            ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
            mPhaseIntervalSetting = Phase::SAMPLING;
            mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
            mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns);
            return;
        }

        if (mPhaseIntervalSetting == Phase::SAMPLING) {
            mPhaseIntervalSetting = Phase::ZERO;
            mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
            mVSyncSource->setDuration(0ns, 0ns);
            stopVsyncListenerLocked();
            lock.unlock();
            mRegionSamplingThread.notifySamplingOffset();
@@ -153,16 +157,15 @@ private:
    }

    RegionSamplingThread& mRegionSamplingThread;
    Scheduler& mScheduler;
    const std::chrono::nanoseconds mTargetSamplingOffset;
    const std::chrono::nanoseconds mTargetSamplingWorkDuration;
    mutable std::mutex mMutex;
    nsecs_t mLastCallbackTime = 0;
    enum class Phase {
        ZERO,
        SAMPLING
    } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
            = Phase::ZERO;
    bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
    std::unique_ptr<VSyncSource> mVSyncSource;
};

RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
@@ -170,11 +173,12 @@ RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& s
      : mFlinger(flinger),
        mScheduler(scheduler),
        mTunables(tunables),
        mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
        mIdleTimer(
                std::chrono::duration_cast<std::chrono::milliseconds>(
                        mTunables.mSamplingTimerTimeout),
                [] {}, [this] { checkForStaleLuma(); }),
        mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
                                                                tunables.mSamplingOffset)),
                                                                tunables.mSamplingDuration)),
        lastSampleTime(0ns) {
    mThread = std::thread([this]() { threadMain(); });
    pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
@@ -183,7 +187,7 @@ RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& s

RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
      : RegionSamplingThread(flinger, scheduler,
                             TimingTunables{defaultRegionSamplingOffset,
                             TimingTunables{defaultRegionSamplingWorkDuration,
                                            defaultRegionSamplingPeriod,
                                            defaultRegionSamplingTimerTimeout}) {}

+4 −4
Original line number Diff line number Diff line
@@ -43,10 +43,10 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st
class RegionSamplingThread : public IBinder::DeathRecipient {
public:
    struct TimingTunables {
        // debug.sf.sampling_offset_ns
        // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
        // at which the sampling should start.
        std::chrono::nanoseconds mSamplingOffset;
        // debug.sf.sampling_duration_ns
        // When asynchronously collecting sample, the duration, at which the sampling should start
        // before a vsync
        std::chrono::nanoseconds mSamplingDuration;
        // debug.sf.sampling_period_ns
        // This is the maximum amount of time the luma recieving client
        // should have to wait for a new luma value after a frame is updated. The inverse of this is
+0 −17
Original line number Diff line number Diff line
@@ -32,32 +32,15 @@ class FenceTime;

class DispSync {
public:
    class Callback {
    public:
        Callback() = default;
        virtual ~Callback() = default;
        virtual void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0;

    protected:
        Callback(Callback const&) = delete;
        Callback& operator=(Callback const&) = delete;
    };

    DispSync() = default;
    virtual ~DispSync() = default;

    virtual void reset() = 0;
    virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
    virtual void beginResync() = 0;
    virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                                 bool* periodFlushed) = 0;
    virtual void endResync() = 0;
    virtual void setPeriod(nsecs_t period) = 0;
    virtual nsecs_t getPeriod() = 0;
    virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback,
                                      nsecs_t lastCallbackTime) = 0;
    virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
    virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0;
    virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0;
    virtual void setIgnorePresentFences(bool ignore) = 0;
    virtual nsecs_t expectedPresentTime(nsecs_t now) = 0;
+119 −42
Original line number Diff line number Diff line
@@ -14,10 +14,6 @@
 * limitations under the License.
 */

// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"

#define ATRACE_TAG ATRACE_TAG_GRAPHICS

#include "DispSyncSource.h"
@@ -29,34 +25,129 @@
#include "DispSync.h"
#include "EventThread.h"

namespace android {
namespace android::scheduler {
using base::StringAppendF;
using namespace std::chrono_literals;

// 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, VSyncDispatch::Callback cb, const char* name,
                     std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
                     std::chrono::nanoseconds notBefore)
          : mName(name),
            mCallback(cb),
            mRegistration(dispatch,
                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
                                    std::placeholders::_2, std::placeholders::_3),
                          mName),
            mStarted(false),
            mWorkDuration(workDuration),
            mReadyDuration(readyDuration),
            mLastCallTime(notBefore) {}

    ~CallbackRepeater() {
        std::lock_guard lock(mMutex);
        mRegistration.cancel();
    }

    void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
        std::lock_guard lock(mMutex);
        mStarted = true;
        mWorkDuration = workDuration;
        mReadyDuration = readyDuration;

        auto const scheduleResult =
                mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                        .readyDuration = mReadyDuration.count(),
                                        .earliestVsync = mLastCallTime.count()});
        LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled),
                            "Error scheduling callback: rc %X", scheduleResult);
    }

    void stop() {
        std::lock_guard lock(mMutex);
        LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
        mStarted = false;
        mRegistration.cancel();
    }

    void dump(std::string& result) const {
        std::lock_guard lock(mMutex);
        const auto relativeLastCallTime =
                mLastCallTime - std::chrono::steady_clock::now().time_since_epoch();
        StringAppendF(&result, "\t%s: ", mName.c_str());
        StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
                      mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f);
        StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f,
                      mStarted ? "running" : "stopped");
    }

private:
    void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
        {
            std::lock_guard lock(mMutex);
            mLastCallTime = std::chrono::nanoseconds(vsyncTime);
        }

DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
        mCallback(vsyncTime, wakeupTime, readyTime);

        {
            std::lock_guard lock(mMutex);
            if (!mStarted) {
                return;
            }
            auto const scheduleResult =
                    mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                            .readyDuration = mReadyDuration.count(),
                                            .earliestVsync = vsyncTime});
            LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled),
                                "Error rescheduling callback: rc %X", scheduleResult);
        }
    }

    const std::string mName;
    scheduler::VSyncDispatch::Callback mCallback;

    mutable std::mutex mMutex;
    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
    bool mStarted GUARDED_BY(mMutex) = false;
    std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns;
    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns;
    std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns;
};

DispSyncSource::DispSyncSource(scheduler::VSyncDispatch& vSyncDispatch,
                               std::chrono::nanoseconds workDuration,
                               std::chrono::nanoseconds readyDuration, bool traceVsync,
                               const char* name)
      : mName(name),
        mValue(base::StringPrintf("VSYNC-%s", name), 0),
        mTraceVsync(traceVsync),
        mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
        mDispSync(dispSync),
        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
        mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration),
        mReadyDuration(readyDuration) {
    mCallbackRepeater =
            std::make_unique<CallbackRepeater>(vSyncDispatch,
                                               std::bind(&DispSyncSource::onVsyncCallback, this,
                                                         std::placeholders::_1,
                                                         std::placeholders::_2,
                                                         std::placeholders::_3),
                                               name, workDuration, readyDuration,
                                               std::chrono::steady_clock::now().time_since_epoch());
}

DispSyncSource::~DispSyncSource() = default;

void DispSyncSource::setVSyncEnabled(bool enable) {
    std::lock_guard lock(mVsyncMutex);
    if (enable) {
        status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                                                   static_cast<DispSync::Callback*>(this),
                                                   mLastCallbackTime);
        if (err != NO_ERROR) {
            ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err);
        }
        mCallbackRepeater->start(mWorkDuration, mReadyDuration);
        // ATRACE_INT(mVsyncOnLabel.c_str(), 1);
    } else {
        status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this),
                                                      &mLastCallbackTime);
        if (err != NO_ERROR) {
            ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err);
        }
        mCallbackRepeater->stop();
        // ATRACE_INT(mVsyncOnLabel.c_str(), 0);
    }
    mEnabled = enable;
@@ -67,32 +158,22 @@ void DispSyncSource::setCallback(VSyncSource::Callback* callback) {
    mCallback = callback;
}

void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
                                 std::chrono::nanoseconds readyDuration) {
    std::lock_guard lock(mVsyncMutex);
    const nsecs_t period = mDispSync->getPeriod();

    // Normalize phaseOffset to [-period, period)
    const int numPeriods = phaseOffset / period;
    phaseOffset -= numPeriods * period;
    if (mPhaseOffset == phaseOffset) {
        return;
    }

    mPhaseOffset = phaseOffset;
    mWorkDuration = workDuration;
    mReadyDuration = readyDuration;

    // If we're not enabled, we don't need to mess with the listeners
    if (!mEnabled) {
        return;
    }

    status_t err =
            mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset);
    if (err != NO_ERROR) {
        ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
    }
    mCallbackRepeater->start(mWorkDuration, mReadyDuration);
}

void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) {
void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
                                     nsecs_t readyTime) {
    VSyncSource::Callback* callback;
    {
        std::lock_guard lock(mCallbackMutex);
@@ -104,17 +185,13 @@ void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestam
    }

    if (callback != nullptr) {
        callback->onVSyncEvent(when, expectedVSyncTimestamp);
        callback->onVSyncEvent(targetWakeupTime, vsyncTime, readyTime);
    }
}

void DispSyncSource::dump(std::string& result) const {
    std::lock_guard lock(mVsyncMutex);
    StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
    mDispSync->dump(result);
}

} // namespace android

// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
} // namespace android::scheduler
+14 −12
Original line number Diff line number Diff line
@@ -18,45 +18,47 @@
#include <mutex>
#include <string>

#include "DispSync.h"
#include "EventThread.h"
#include "TracedOrdinal.h"
#include "VSyncDispatch.h"

namespace android {
namespace android::scheduler {
class CallbackRepeater;

class DispSyncSource final : public VSyncSource, private DispSync::Callback {
class DispSyncSource final : public VSyncSource {
public:
    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
    DispSyncSource(VSyncDispatch& vSyncDispatch, std::chrono::nanoseconds workDuration,
                   std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name);

    ~DispSyncSource() override = default;
    ~DispSyncSource() override;

    // The following methods are implementation of VSyncSource.
    const char* getName() const override { return mName; }
    void setVSyncEnabled(bool enable) override;
    void setCallback(VSyncSource::Callback* callback) override;
    void setPhaseOffset(nsecs_t phaseOffset) override;
    void setDuration(std::chrono::nanoseconds workDuration,
                     std::chrono::nanoseconds readyDuration) override;

    void dump(std::string&) const override;

private:
    // The following method is the implementation of the DispSync::Callback.
    void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override;
    void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime);

    const char* const mName;
    TracedOrdinal<int> mValue;

    const bool mTraceVsync;
    const std::string mVsyncOnLabel;
    nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;

    DispSync* mDispSync;
    std::unique_ptr<CallbackRepeater> mCallbackRepeater;

    std::mutex mCallbackMutex;
    VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;

    mutable std::mutex mVsyncMutex;
    TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
    TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex);
    std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex);
    bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};

} // namespace android
} // namespace android::scheduler
Loading