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

Commit e203e04e authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Allow for more flexible vsync-offsets

The issue with just one single sf early offset is that this may be
too early in case of steady rendering, i.e. when a
Wide-Color-Gamut window is on screen. Instead, we allow for
separate offsets in case we are in GL comp but the transaction
wasn't marked as early, and when the transaction is marked as
early.

In addition to that, we also allow the app-vsync to be adjusted
in these scenarios.

Bug: 110112323
Change-Id: I26d73b88b4e9e609ceedb604e8338452d9a89093
Merged-In: I26d73b88b4e9e609ceedb604e8338452d9a89093
parent ba88c2e9
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -222,7 +222,13 @@ public:
                // Pretend that the last time this event was handled at the same frame but with the
                // new offset to allow for a seamless offset change without double-firing or
                // skipping.
                listener.mLastEventTime -= (oldPhase - phase);
                nsecs_t diff = oldPhase - phase;
                if (diff > mPeriod / 2) {
                    diff -= mPeriod;
                } else if (diff < -mPeriod / 2) {
                    diff += mPeriod;
                }
                listener.mLastEventTime -= diff;
                mCond.signal();
                return NO_ERROR;
            }
+37 −9
Original line number Diff line number Diff line
@@ -331,11 +331,26 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) {
    auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
    mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;

    property_get("debug.sf.early_phase_offset_ns", value, "0");
    const int earlyWakeupOffsetOffsetNs = atoi(value);
    ALOGI_IF(earlyWakeupOffsetOffsetNs != 0, "Enabling separate early offset");
    mVsyncModulator.setPhaseOffsets(sfVsyncPhaseOffsetNs - earlyWakeupOffsetOffsetNs,
            sfVsyncPhaseOffsetNs);
    property_get("debug.sf.early_phase_offset_ns", value, "-1");
    const int earlySfOffsetNs = atoi(value);

    property_get("debug.sf.early_gl_phase_offset_ns", value, "-1");
    const int earlyGlSfOffsetNs = atoi(value);

    property_get("debug.sf.early_app_phase_offset_ns", value, "-1");
    const int earlyAppOffsetNs = atoi(value);

    property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1");
    const int earlyGlAppOffsetNs = atoi(value);

    const VSyncModulator::Offsets earlyOffsets =
            {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
            earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
    const VSyncModulator::Offsets earlyGlOffsets =
            {earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
            earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
    mVsyncModulator.setPhaseOffsets(earlyOffsets, earlyGlOffsets,
            {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs});

    // We should be reading 'persist.sys.sf.color_saturation' here
    // but since /data may be encrypted, we need to wait until after vold
@@ -662,7 +677,7 @@ void SurfaceFlinger::init() {
                                                },
                                                "sfEventThread");
    mEventQueue->setEventThread(mSFEventThread.get());
    mVsyncModulator.setEventThread(mSFEventThread.get());
    mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get());

    // Get a RenderEngine for the given display / config (can't fail)
    getBE().mRenderEngine =
@@ -4207,9 +4222,22 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
    colorizer.bold(result);
    result.append("DispSync configuration: ");
    colorizer.reset(result);
    result.appendFormat("app phase %" PRId64 " ns, sf phase %" PRId64 " ns, early sf phase %" PRId64
        " ns, present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
        vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, mVsyncModulator.getEarlyPhaseOffset(),
    const auto [sfEarlyOffset, appEarlyOffset] = mVsyncModulator.getEarlyOffsets();
    const auto [sfEarlyGlOffset, appEarlyGlOffset] = mVsyncModulator.getEarlyGlOffsets();
    result.appendFormat(
        "app phase %" PRId64 " ns, "
        "sf phase %" PRId64 " ns, "
        "early app phase %" PRId64 " ns, "
        "early sf phase %" PRId64 " ns, "
        "early app gl phase %" PRId64 " ns, "
        "early sf gl phase %" PRId64 " ns, "
        "present offset %" PRId64 " ns (refresh %" PRId64 " ns)",
        vsyncPhaseOffsetNs,
        sfVsyncPhaseOffsetNs,
        appEarlyOffset,
        sfEarlyOffset,
        appEarlyGlOffset,
        sfEarlyOffset,
        dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod());
    result.append("\n");

+65 −42
Original line number Diff line number Diff line
@@ -36,6 +36,11 @@ private:

public:

    struct Offsets {
        nsecs_t sf;
        nsecs_t app;
    };

    enum TransactionStart {
        EARLY,
        NORMAL
@@ -43,21 +48,32 @@ public:

    // Sets the phase offsets
    //
    // early: the phase offset when waking up early. May be the same as late, in which case we don't
    //        shift offsets.
    // late: the regular sf phase offset.
    void setPhaseOffsets(nsecs_t early, nsecs_t late) {
        mEarlyPhaseOffset = early;
        mLatePhaseOffset = late;
        mPhaseOffset = late;
    // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
    //          as early. May be the same as late, in which case we don't shift offsets.
    // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
    //            and the transaction was marked as early, we'll use sfEarly.
    // sfLate: The regular SF vsync phase offset.
    // appEarly: Like sfEarly, but for the app-vsync
    // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
    // appLate: The regular app vsync phase offset.
    void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late) {
        mEarlyOffsets = early;
        mEarlyGlOffsets = earlyGl;
        mLateOffsets = late;
        mOffsets = late;
    }

    Offsets getEarlyOffsets() const {
        return mEarlyOffsets;
    }

    nsecs_t getEarlyPhaseOffset() const {
        return mEarlyPhaseOffset;
    Offsets getEarlyGlOffsets() const {
        return mEarlyGlOffsets;
    }

    void setEventThread(EventThread* eventThread) {
        mEventThread = eventThread;
    void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) {
        mSfEventThread = sfEventThread;
        mAppEventThread = appEventThread;
    }

    void setTransactionStart(TransactionStart transactionStart) {
@@ -71,63 +87,70 @@ public:
            return;
        }
        mTransactionStart = transactionStart;
        updatePhaseOffsets();
        updateOffsets();
    }

    void onTransactionHandled() {
        if (mTransactionStart == TransactionStart::NORMAL) return;
        mTransactionStart = TransactionStart::NORMAL;
        updatePhaseOffsets();
        updateOffsets();
    }

    void onRefreshed(bool usedRenderEngine) {
        bool updatePhaseOffsetsNeeded = false;
        bool updateOffsetsNeeded = false;
        if (mRemainingEarlyFrameCount > 0) {
            mRemainingEarlyFrameCount--;
            updatePhaseOffsetsNeeded = true;
            updateOffsetsNeeded = true;
        }
        if (usedRenderEngine != mLastFrameUsedRenderEngine) {
            mLastFrameUsedRenderEngine = usedRenderEngine;
            updatePhaseOffsetsNeeded = true;
            updateOffsetsNeeded = true;
        }
        if (updatePhaseOffsetsNeeded) {
            updatePhaseOffsets();
        if (updateOffsetsNeeded) {
            updateOffsets();
        }
    }

private:

    void updatePhaseOffsets() {

        // Do not change phase offsets if disabled.
        if (mEarlyPhaseOffset == mLatePhaseOffset) return;
    void updateOffsets() {
        const Offsets desired = getOffsets();
        const Offsets current = mOffsets;

        if (shouldUseEarlyOffset()) {
            if (mPhaseOffset != mEarlyPhaseOffset) {
                if (mEventThread) {
                    mEventThread->setPhaseOffset(mEarlyPhaseOffset);
        bool changed = false;
        if (desired.sf != current.sf) {
            mSfEventThread->setPhaseOffset(desired.sf);
            changed = true;
        }
                mPhaseOffset = mEarlyPhaseOffset;
        if (desired.app != current.app) {
            mAppEventThread->setPhaseOffset(desired.app);
            changed = true;
        }
        } else {
            if (mPhaseOffset != mLatePhaseOffset) {
                if (mEventThread) {
                    mEventThread->setPhaseOffset(mLatePhaseOffset);

        if (changed) {
            mOffsets = desired;
        }
                mPhaseOffset = mLatePhaseOffset;
    }

    Offsets getOffsets() {
        if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) {
            return mEarlyOffsets;
        } else if (mLastFrameUsedRenderEngine) {
            return mEarlyGlOffsets;
        } else {
            return mLateOffsets;
        }
    }

    bool shouldUseEarlyOffset() {
        return mTransactionStart == TransactionStart::EARLY || mLastFrameUsedRenderEngine
                || mRemainingEarlyFrameCount > 0;
    }
    Offsets mLateOffsets;
    Offsets mEarlyOffsets;
    Offsets mEarlyGlOffsets;

    EventThread* mSfEventThread = nullptr;
    EventThread* mAppEventThread = nullptr;

    std::atomic<Offsets> mOffsets;

    nsecs_t mLatePhaseOffset = 0;
    nsecs_t mEarlyPhaseOffset = 0;
    EventThread* mEventThread = nullptr;
    std::atomic<nsecs_t> mPhaseOffset = 0;
    std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL;
    std::atomic<bool> mLastFrameUsedRenderEngine = false;
    std::atomic<int> mRemainingEarlyFrameCount = 0;