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

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

SurfaceFlinger: add explicit eEarlyWakeup start and end

The current handling of eEarlyWakeup is using 2 frames of timeout
in SurfaceFlinger to know when we are no longer in early offset config.
There are few cases where a transaction is dropped or delayed (usually
caused by the offset change itself) which results in switching back
and forth from early offset to non-early offset. This change adds two
new flags, eExplicitEarlyWakeupStart and eExplicitEarlyWakeupEnd that
will be used by WindowManager to indicate when to enter early offset
and when to exit. With these explicit flags, the timings on transaction
no longer matters and we consistently stay at early offset config for the desired
duration.

Bug: 158127834
Test: Quick switch between apps and verify that offset doesn't change
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Change-Id: Ie10af30a2d8c0f4f21ac7ffed469a74e1bf8dbc1
parent 680edcf1
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -353,6 +353,8 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
        mTransactionNestCount(other.mTransactionNestCount),
        mAnimation(other.mAnimation),
        mEarlyWakeup(other.mEarlyWakeup),
        mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart),
        mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd),
        mContainsBuffer(other.mContainsBuffer),
        mDesiredPresentTime(other.mDesiredPresentTime) {
    mDisplayStates = other.mDisplayStates;
@@ -375,6 +377,8 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
    const uint32_t transactionNestCount = parcel->readUint32();
    const bool animation = parcel->readBool();
    const bool earlyWakeup = parcel->readBool();
    const bool explicitEarlyWakeupStart = parcel->readBool();
    const bool explicitEarlyWakeupEnd = parcel->readBool();
    const bool containsBuffer = parcel->readBool();
    const int64_t desiredPresentTime = parcel->readInt64();

@@ -443,6 +447,8 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
    mTransactionNestCount = transactionNestCount;
    mAnimation = animation;
    mEarlyWakeup = earlyWakeup;
    mExplicitEarlyWakeupStart = explicitEarlyWakeupStart;
    mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd;
    mContainsBuffer = containsBuffer;
    mDesiredPresentTime = desiredPresentTime;
    mDisplayStates = displayStates;
@@ -470,6 +476,8 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
    parcel->writeUint32(mTransactionNestCount);
    parcel->writeBool(mAnimation);
    parcel->writeBool(mEarlyWakeup);
    parcel->writeBool(mExplicitEarlyWakeupStart);
    parcel->writeBool(mExplicitEarlyWakeupEnd);
    parcel->writeBool(mContainsBuffer);
    parcel->writeInt64(mDesiredPresentTime);
    parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
@@ -545,6 +553,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr

    mContainsBuffer |= other.mContainsBuffer;
    mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
    mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart;
    mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd;
    other.clear();
    return *this;
}
@@ -559,6 +569,8 @@ void SurfaceComposerClient::Transaction::clear() {
    mTransactionNestCount = 0;
    mAnimation = false;
    mEarlyWakeup = false;
    mExplicitEarlyWakeupStart = false;
    mExplicitEarlyWakeupEnd = false;
    mDesiredPresentTime = -1;
}

@@ -682,9 +694,20 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) {
        flags |= ISurfaceComposer::eEarlyWakeup;
    }

    // If both mExplicitEarlyWakeupStart and mExplicitEarlyWakeupEnd are set
    // it is equivalent for none
    if (mExplicitEarlyWakeupStart && !mExplicitEarlyWakeupEnd) {
        flags |= ISurfaceComposer::eExplicitEarlyWakeupStart;
    }
    if (mExplicitEarlyWakeupEnd && !mExplicitEarlyWakeupStart) {
        flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd;
    }

    mForceSynchronous = false;
    mAnimation = false;
    mEarlyWakeup = false;
    mExplicitEarlyWakeupStart = false;
    mExplicitEarlyWakeupEnd = false;

    sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
    sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
@@ -731,6 +754,14 @@ void SurfaceComposerClient::Transaction::setEarlyWakeup() {
    mEarlyWakeup = true;
}

void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupStart() {
    mExplicitEarlyWakeupStart = true;
}

void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() {
    mExplicitEarlyWakeupEnd = true;
}

layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
    if (mComposerStates.count(handle) == 0) {
        // we don't have it, add an initialized layer_state to our list
+14 −6
Original line number Diff line number Diff line
@@ -83,10 +83,18 @@ public:
        eSynchronous = 0x01,
        eAnimation = 0x02,

        // Indicates that this transaction will likely result in a lot of layers being composed, and
        // thus, SurfaceFlinger should wake-up earlier to avoid missing frame deadlines. In this
        // case SurfaceFlinger will wake up at (sf vsync offset - debug.sf.early_phase_offset_ns)
        eEarlyWakeup = 0x04
        // DEPRECATED - use eExplicitEarlyWakeup[Start|End]
        eEarlyWakeup = 0x04,

        // Explicit indication that this transaction and others to follow will likely result in a
        // lot of layers being composed, and thus, SurfaceFlinger should wake-up earlier to avoid
        // missing frame deadlines. In this case SurfaceFlinger will wake up at
        // (sf vsync offset - debug.sf.early_phase_offset_ns). SurfaceFlinger will continue to be
        // in the early configuration until it receives eExplicitEarlyWakeupEnd. These flags are
        // expected to be used by WindowManager only and are guarded by
        // android.permission.ACCESS_SURFACE_FLINGER
        eExplicitEarlyWakeupStart = 0x08,
        eExplicitEarlyWakeupEnd = 0x10,
    };

    enum VsyncSource {
+8 −4
Original line number Diff line number Diff line
@@ -349,6 +349,8 @@ public:
        uint32_t mTransactionNestCount = 0;
        bool mAnimation = false;
        bool mEarlyWakeup = false;
        bool mExplicitEarlyWakeupStart = false;
        bool mExplicitEarlyWakeupEnd = false;

        // Indicates that the Transaction contains a buffer that should be cached
        bool mContainsBuffer = false;
@@ -547,6 +549,8 @@ public:
        void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
        void setAnimationTransaction();
        void setEarlyWakeup();
        void setExplicitEarlyWakeupStart();
        void setExplicitEarlyWakeupEnd();
    };

    status_t clearLayerFrameStats(const sp<IBinder>& token) const;
+14 −3
Original line number Diff line number Diff line
@@ -55,13 +55,24 @@ public:
    virtual void kernelTimerChanged(bool expired) = 0;
};

class Scheduler {
class IPhaseOffsetControl {
public:
    virtual ~IPhaseOffsetControl() = default;
    virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0;
};

class Scheduler : public IPhaseOffsetControl {
public:
    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
    using ConfigEvent = scheduler::RefreshRateConfigEvent;

    // Indicates whether to start the transaction early, or at vsync time.
    enum class TransactionStart { EARLY, NORMAL };
    enum class TransactionStart {
        Early,      // DEPRECATED. Start the transaction early. Times out on its own
        EarlyStart, // Start the transaction early and keep this config until EarlyEnd
        EarlyEnd,   // End the early config started at EarlyStart
        Normal      // Start the transaction at the normal time
    };

    Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
@@ -90,7 +101,7 @@ public:
    void onScreenReleased(ConnectionHandle);

    // Modifies phase offset in the event thread.
    void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
    void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override;

    void getDisplayStatInfo(DisplayStatInfo* stats);

+33 −13
Original line number Diff line number Diff line
@@ -31,11 +31,11 @@

namespace android::scheduler {

VSyncModulator::VSyncModulator(Scheduler& scheduler,
VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl,
                               Scheduler::ConnectionHandle appConnectionHandle,
                               Scheduler::ConnectionHandle sfConnectionHandle,
                               const OffsetsConfig& config)
      : mScheduler(scheduler),
      : mPhaseOffsetControl(phaseOffsetControl),
        mAppConnectionHandle(appConnectionHandle),
        mSfConnectionHandle(sfConnectionHandle),
        mOffsetsConfig(config) {
@@ -51,14 +51,35 @@ void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
}

void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) {
    if (transactionStart == Scheduler::TransactionStart::EARLY) {
    switch (transactionStart) {
        case Scheduler::TransactionStart::EarlyStart:
            ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart");
            mExplicitEarlyWakeup = true;
            break;
        case Scheduler::TransactionStart::EarlyEnd:
            ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart");
            mExplicitEarlyWakeup = false;
            break;
        case Scheduler::TransactionStart::Normal:
        case Scheduler::TransactionStart::Early:
            // Non explicit don't change the explicit early wakeup state
            break;
    }

    if (mTraceDetailedInfo) {
        ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup);
    }

    if (!mExplicitEarlyWakeup &&
        (transactionStart == Scheduler::TransactionStart::Early ||
         transactionStart == Scheduler::TransactionStart::EarlyEnd)) {
        mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION;
        mEarlyTxnStartTime = std::chrono::steady_clock::now();
    }

    // An early transaction stays an early transaction.
    if (transactionStart == mTransactionStart ||
        mTransactionStart == Scheduler::TransactionStart::EARLY) {
        mTransactionStart == Scheduler::TransactionStart::EarlyEnd) {
        return;
    }
    mTransactionStart = transactionStart;
@@ -67,8 +88,8 @@ void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transaction

void VSyncModulator::onTransactionHandled() {
    mTxnAppliedTime = std::chrono::steady_clock::now();
    if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return;
    mTransactionStart = Scheduler::TransactionStart::NORMAL;
    if (mTransactionStart == Scheduler::TransactionStart::Normal) return;
    mTransactionStart = Scheduler::TransactionStart::Normal;
    updateOffsets();
}

@@ -91,11 +112,10 @@ void VSyncModulator::onRefreshRateChangeCompleted() {
void VSyncModulator::onRefreshed(bool usedRenderEngine) {
    bool updateOffsetsNeeded = false;

    // Apply a 1ms margin to account for potential data races
    // Apply a margin to account for potential data races
    // This might make us stay in early offsets for one
    // additional frame but it's better to be conservative here.
    static const constexpr std::chrono::nanoseconds kMargin = 1ms;
    if ((mEarlyTxnStartTime.load() + kMargin) < mTxnAppliedTime.load()) {
    if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) {
        if (mRemainingEarlyFrameCount > 0) {
            mRemainingEarlyFrameCount--;
            updateOffsetsNeeded = true;
@@ -121,8 +141,8 @@ VSyncModulator::Offsets VSyncModulator::getOffsets() const {
const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
    // Early offsets are used if we're in the middle of a refresh rate
    // change, or if we recently begin a transaction.
    if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
        mRefreshRateChangePending) {
    if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd ||
        mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) {
        return mOffsetsConfig.early;
    } else if (mRemainingRenderEngineUsageCount > 0) {
        return mOffsetsConfig.earlyGl;
@@ -139,8 +159,8 @@ void VSyncModulator::updateOffsets() {
void VSyncModulator::updateOffsetsLocked() {
    const Offsets& offsets = getNextOffsets();

    mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
    mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
    mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf);
    mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app);

    mOffsets = offsets;

Loading