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

Commit a5bdd9d8 authored by Ana Krulec's avatar Ana Krulec
Browse files

SF: set active config state machine

1) Set the desired active config bit
2) On the next vsync, ask HWC to update config.
3) Send a refresh to HWC
4) When the fence comes back, update SF to the new config/refresh rate
   and trigger HWC vsync.

see go/surface-flinger-scheduler for more info

Test: Systrace.
Change-Id: I4ed649668cc6cc23cd3f428b3c92ab1c199c8aa4
parent 13fb8861
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -69,7 +69,7 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function)
    mEventControlThread = std::make_unique<impl::EventControlThread>(function);

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.set_idle_timer_ms", value, "30");
    property_get("debug.sf.set_idle_timer_ms", value, "0");
    mSetIdleTimerMs = atoi(value);

    if (mSetIdleTimerMs > 0) {
+96 −28
Original line number Diff line number Diff line
@@ -822,14 +822,12 @@ status_t SurfaceFlinger::getSupportedFrameTimestamps(
    return NO_ERROR;
}

status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
status_t SurfaceFlinger::getDisplayConfigsLocked(const sp<IBinder>& displayToken,
                                                 Vector<DisplayInfo>* configs) {
    if (!displayToken || !configs) {
        return BAD_VALUE;
    }

    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);

    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
    if (!displayId) {
        return NAME_NOT_FOUND;
@@ -957,22 +955,19 @@ int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) {
    return display->getActiveConfig();
}

status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
    ATRACE_NAME("setActiveConfigSync");
    postMessageSync(new LambdaMessage(
            [&]() NO_THREAD_SAFETY_ANALYSIS { setActiveConfigInternal(displayToken, mode); }));
    return NO_ERROR;
}
void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) {
    ATRACE_CALL();

void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
    Vector<DisplayInfo> configs;
    getDisplayConfigs(displayToken, &configs);
    // Lock is acquired by setRefreshRateTo.
    getDisplayConfigsLocked(displayToken, &configs);
    if (mode < 0 || mode >= static_cast<int>(configs.size())) {
        ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
        return;
    }

    const auto display = getDisplayDevice(displayToken);
    // Lock is acquired by setRefreshRateTo.
    const auto display = getDisplayDeviceLocked(displayToken);
    if (!display) {
        ALOGE("Attempt to set active config %d for invalid display token %p", mode,
              displayToken.get());
@@ -988,23 +983,95 @@ void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, in
        return;
    }

    int currentMode = display->getActiveConfig();
    if (mode == currentMode) {
        // Don't update config if we are already running in the desired mode.
        return;
    // Don't check against the current mode yet. Worst case we set the desired
    // config twice.
    {
        std::lock_guard<std::mutex> lock(mActiveConfigLock);
        mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken};
    }
    if (mUseScheduler) {
        mRefreshRateStats->setConfigMode(mode);
    // This will trigger HWC refresh without resetting the idle timer.
    repaintEverythingForHWC();
}

status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
    ATRACE_CALL();
    postMessageSync(new LambdaMessage(
            [&]() NO_THREAD_SAFETY_ANALYSIS { setDesiredActiveConfig(displayToken, mode); }));
    return NO_ERROR;
}

void SurfaceFlinger::setActiveConfigInHWC() {
    ATRACE_CALL();

    const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken);
    if (!display) {
        return;
    }
    const auto displayId = display->getId();
    LOG_ALWAYS_FATAL_IF(!displayId);

    display->setActiveConfig(mode);
    getHwComposer().setActiveConfig(*displayId, mode);
    ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
    mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC;
    ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
}

void SurfaceFlinger::setActiveConfigInternal() {
    ATRACE_CALL();

    std::lock_guard<std::mutex> lock(mActiveConfigLock);
    if (mUseScheduler) {
        mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
    }

    const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken);
    display->setActiveConfig(mUpcomingActiveConfig.configId);

    mSetActiveConfigState = SetActiveConfigState::NONE;
    ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);

    ATRACE_INT("ActiveConfigMode", mode);
    resyncToHardwareVsync(true, getVsyncPeriod());
    ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
}

bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS {
    // Store the local variable to release the lock.
    ActiveConfigInfo desiredActiveConfig;
    {
        std::lock_guard<std::mutex> lock(mActiveConfigLock);
        desiredActiveConfig = mDesiredActiveConfig;
    }

    const auto display = getDisplayDevice(desiredActiveConfig.displayToken);
    if (display) {
        if (mSetActiveConfigState == SetActiveConfigState::NONE &&
            display->getActiveConfig() != desiredActiveConfig.configId) {
            // Step 1) Desired active config was set, it is different than the
            // config currently in use. Notify HWC.
            mUpcomingActiveConfig = desiredActiveConfig;
            setActiveConfigInHWC();
        } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) {
            // Step 2) HWC was notified and we received refresh from it.
            mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED;
            ATRACE_INT("SetActiveConfigState", mSetActiveConfigState);
            repaintEverythingForHWC();
            // We do not want to give another frame to HWC while we are transitioning.
            return false;
        } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED &&
                   !(mPreviousPresentFence != Fence::NO_FENCE &&
                     (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) {
            // Step 3) We received the present fence from the HWC, so we assume it
            // successfully updated the config, hence we update SF.
            setActiveConfigInternal();
            // If the config changed again while we were transitioning, restart the
            // process.
            if (desiredActiveConfig != mUpcomingActiveConfig) {
                mUpcomingActiveConfig = desiredActiveConfig;
                setActiveConfigInHWC(); // sets the state to NOTIFY_HWC
            }
        }
    }
    return true;
}

status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1482,8 +1549,7 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) {
        // TODO(b/113612090): There should be a better way at determining which config
        // has the right refresh rate.
        if (std::abs(fps - newFps) <= 1) {
            setActiveConfigInternal(getInternalDisplayTokenLocked(), i);
            ATRACE_INT("FPS", newFps);
            setDesiredActiveConfig(getInternalDisplayTokenLocked(), i);
        }
    }
}
@@ -1627,14 +1693,16 @@ void SurfaceFlinger::onMessageReceived(int32_t what) {
    ATRACE_CALL();
    switch (what) {
        case MessageQueue::INVALIDATE: {
            if (!updateSetActiveConfigStateMachine()) {
                break;
            }

            if (mUseScheduler) {
                // This call is made each time SF wakes up and creates a new frame.
                mScheduler->incrementFrameCounter();
            }
            bool frameMissed = !mHadClientComposition &&
                    mPreviousPresentFence != Fence::NO_FENCE &&
                    (mPreviousPresentFence->getSignalTime() ==
                            Fence::SIGNAL_TIME_PENDING);
            bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE &&
                    (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
            mFrameMissedCount += frameMissed;
            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
            if (frameMissed) {
+46 −3
Original line number Diff line number Diff line
@@ -437,7 +437,12 @@ private:
                           const Rect& sourceCrop, float frameScale, bool childrenOnly) override;
    status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override;
    status_t getDisplayConfigs(const sp<IBinder>& displayToken,
                               Vector<DisplayInfo>* configs) override;
                               Vector<DisplayInfo>* configs) override {
        Mutex::Autolock _l(mStateLock);
        return getDisplayConfigsLocked(displayToken, configs);
    }
    status_t getDisplayConfigsLocked(const sp<IBinder>& displayToken, Vector<DisplayInfo>* configs)
            REQUIRES(mStateLock);
    int getActiveConfig(const sp<IBinder>& displayToken) override;
    status_t getDisplayColorModes(const sp<IBinder>& displayToken,
                                  Vector<ui::ColorMode>* configs) override;
@@ -503,8 +508,18 @@ private:

    // called on the main thread in response to initializeDisplays()
    void onInitializeDisplays() REQUIRES(mStateLock);
    // called on the main thread in response to setActiveConfig()
    void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
    // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
    void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock);
    // Calls setActiveConfig in HWC.
    void setActiveConfigInHWC();
    // Once HWC has returned the present fence, this sets the active config and a new refresh
    // rate in SF. It also triggers HWC vsync.
    void setActiveConfigInternal() REQUIRES(mStateLock);
    // Active config is updated on INVALIDATE call in a state machine-like manner. When the
    // desired config was set, HWC needs to update the pannel on the next refresh, and when
    // we receive the fence back, we know that the process was complete. It returns whether
    // the invalidate process should continue.
    bool updateSetActiveConfigStateMachine();
    // called on the main thread in response to setPowerMode()
    void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);

@@ -1084,6 +1099,34 @@ private:
    sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;

    // The following structs are used for configuring active config state at a desired time,
    // which is once per vsync at invalidate time.
    enum SetActiveConfigState {
        NONE,            // not in progress
        NOTIFIED_HWC,    // call to HWC has been made
        REFRESH_RECEIVED // onRefresh was received from HWC
    };
    std::atomic<SetActiveConfigState> mSetActiveConfigState = SetActiveConfigState::NONE;

    struct ActiveConfigInfo {
        int configId;
        sp<IBinder> displayToken;

        bool operator!=(const ActiveConfigInfo& other) const {
            if (configId != other.configId) {
                return true;
            }
            return (displayToken != other.displayToken);
        }
    };
    std::mutex mActiveConfigLock;
    // This bit is set once we start setting the config. We read from this bit during the
    // process. If at the end, this bit is different than mDesiredActiveConfig, we restart
    // the process.
    ActiveConfigInfo mUpcomingActiveConfig; // Always read and written on the main thread.
    // This bit can be set at any point in time when the system wants the new config.
    ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);

    /* ------------------------------------------------------------------------ */
    sp<IInputFlinger> mInputFlinger;