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

Commit 567be130 authored by Matt Buckley's avatar Matt Buckley Committed by Android (Google) Code Review
Browse files

Merge "Expanded poweradvisor to handle adpf hint sessions"

parents 02476bb7 06f299a2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ cc_defaults {
        "android.hardware.graphics.composer@2.4",
        "android.hardware.power@1.0",
        "android.hardware.power@1.3",
        "android.hardware.power-V1-cpp",
        "android.hardware.power-V2-cpp",
        "libbase",
        "libbinder",
        "libcutils",
+15 −5
Original line number Diff line number Diff line
@@ -29,11 +29,21 @@ public:
    PowerAdvisor();
    ~PowerAdvisor() override;

    MOCK_METHOD0(init, void());
    MOCK_METHOD0(onBootFinished, void());
    MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
    MOCK_METHOD0(isUsingExpensiveRendering, bool());
    MOCK_METHOD0(notifyDisplayUpdateImminent, void());
    MOCK_METHOD(void, init, (), (override));
    MOCK_METHOD(void, onBootFinished, (), (override));
    MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected),
                (override));
    MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override));
    MOCK_METHOD(void, notifyDisplayUpdateImminent, (), (override));
    MOCK_METHOD(bool, usePowerHintSession, (), (override));
    MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
    MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override));
    MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override));
    MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds),
                (override));
    MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp),
                (override));
    MOCK_METHOD(void, enablePowerHint, (bool enabled), (override));
};

} // namespace mock
+291 −3
Original line number Diff line number Diff line
@@ -19,7 +19,10 @@
#undef LOG_TAG
#define LOG_TAG "PowerAdvisor"

#include <unistd.h>
#include <cinttypes>
#include <cstdint>
#include <optional>

#include <android-base/properties.h>
#include <utils/Log.h>
@@ -27,6 +30,9 @@

#include <android/hardware/power/1.3/IPower.h>
#include <android/hardware/power/IPower.h>
#include <android/hardware/power/IPowerHintSession.h>
#include <android/hardware/power/WorkDuration.h>

#include <binder/IServiceManager.h>

#include "../SurfaceFlingerProperties.h"
@@ -47,10 +53,14 @@ using V1_3::PowerHint;

using android::hardware::power::Boost;
using android::hardware::power::IPower;
using android::hardware::power::IPowerHintSession;
using android::hardware::power::Mode;
using base::GetIntProperty;
using android::hardware::power::WorkDuration;

using scheduler::OneShotTimer;

class AidlPowerHalWrapper;

PowerAdvisor::~PowerAdvisor() = default;

namespace {
@@ -83,6 +93,13 @@ void PowerAdvisor::init() {

void PowerAdvisor::onBootFinished() {
    mBootFinished.store(true);
    {
        std::lock_guard lock(mPowerHalMutex);
        HalWrapper* halWrapper = getPowerHal();
        if (halWrapper != nullptr && usePowerHintSession()) {
            mPowerHintSessionRunning = halWrapper->startPowerHintSession();
        }
    }
}

void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
@@ -136,6 +153,80 @@ void PowerAdvisor::notifyDisplayUpdateImminent() {
    }
}

// checks both if it supports and if it's enabled
bool PowerAdvisor::usePowerHintSession() {
    // uses cached value since the underlying support and flag are unlikely to change at runtime
    ALOGE_IF(!mPowerHintEnabled.has_value(), "Power hint session cannot be used before boot!");
    return mPowerHintEnabled.value_or(false) && supportsPowerHintSession();
}

bool PowerAdvisor::supportsPowerHintSession() {
    // cache to avoid needing lock every time
    if (!mSupportsPowerHint.has_value()) {
        std::lock_guard lock(mPowerHalMutex);
        HalWrapper* const halWrapper = getPowerHal();
        mSupportsPowerHint = halWrapper->supportsPowerHintSession();
    }
    return *mSupportsPowerHint;
}

bool PowerAdvisor::isPowerHintSessionRunning() {
    return mPowerHintSessionRunning;
}

void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) {
    // we check "supports" here not "usePowerHintSession" because this needs to work
    // before the session is actually running, and "use" will always fail before boot
    // we store the values passed in before boot to start the session with during onBootFinished
    if (!supportsPowerHintSession()) {
        ALOGV("Power hint session target duration cannot be set, skipping");
        return;
    }
    {
        std::lock_guard lock(mPowerHalMutex);
        HalWrapper* const halWrapper = getPowerHal();
        if (halWrapper != nullptr) {
            halWrapper->setTargetWorkDuration(targetDurationNanos);
        }
    }
}

void PowerAdvisor::setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) {
    // we check "supports" here not "usePowerHintSession" because this needs to wsork
    // before the session is actually running, and "use" will always fail before boot.
    // we store the values passed in before boot to start the session with during onBootFinished
    if (!supportsPowerHintSession()) {
        ALOGV("Power hint session thread ids cannot be set, skipping");
        return;
    }
    {
        std::lock_guard lock(mPowerHalMutex);
        HalWrapper* const halWrapper = getPowerHal();
        if (halWrapper != nullptr) {
            halWrapper->setPowerHintSessionThreadIds(const_cast<std::vector<int32_t>&>(threadIds));
        }
    }
}

void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) {
    if (!mBootFinished || !usePowerHintSession()) {
        ALOGV("Actual work duration power hint cannot be sent, skipping");
        return;
    }
    {
        std::lock_guard lock(mPowerHalMutex);
        HalWrapper* const halWrapper = getPowerHal();
        if (halWrapper != nullptr) {
            halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos);
        }
    }
}

// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished
void PowerAdvisor::enablePowerHint(bool enabled) {
    mPowerHintEnabled = enabled;
}

class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
public:
    HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {}
@@ -178,6 +269,26 @@ public:
        return true;
    }

    bool supportsPowerHintSession() override { return false; }

    bool isPowerHintSessionRunning() override { return false; }

    void restartPowerHintSession() override {}

    void setPowerHintSessionThreadIds(const std::vector<int32_t>&) override {}

    bool startPowerHintSession() override { return false; }

    void setTargetWorkDuration(int64_t) override {}

    void sendActualWorkDuration(int64_t, nsecs_t) override {}

    bool shouldReconnectHAL() override { return false; }

    std::vector<int32_t> getPowerHintSessionThreadIds() override { return std::vector<int32_t>{}; }

    std::optional<int64_t> getTargetWorkDuration() override { return std::nullopt; }

private:
    const sp<V1_3::IPower> mPowerHal = nullptr;
};
@@ -195,9 +306,21 @@ public:
        if (!ret.isOk()) {
            mHasDisplayUpdateImminent = false;
        }

        // This just gives a number not a binder status, so no .isOk()
        mSupportsPowerHints = mPowerHal->getInterfaceVersion() >= 2;

        if (mSupportsPowerHints) {
            mPowerHintQueue.reserve(MAX_QUEUE_SIZE);
        }
    }

    ~AidlPowerHalWrapper() override = default;
    ~AidlPowerHalWrapper() override {
        if (mPowerHintSession != nullptr) {
            mPowerHintSession->close();
            mPowerHintSession = nullptr;
        }
    };

    static std::unique_ptr<HalWrapper> connect() {
        // This only waits if the service is actually declared
@@ -232,10 +355,147 @@ public:
        return ret.isOk();
    }

    // only version 2+ of the aidl supports power hint sessions, hidl has no support
    bool supportsPowerHintSession() override { return mSupportsPowerHints; }

    bool isPowerHintSessionRunning() override { return mPowerHintSession != nullptr; }

    void closePowerHintSession() {
        if (mPowerHintSession != nullptr) {
            mPowerHintSession->close();
            mPowerHintSession = nullptr;
        }
    }

    void restartPowerHintSession() {
        closePowerHintSession();
        startPowerHintSession();
    }

    void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override {
        if (threadIds != mPowerHintThreadIds) {
            mPowerHintThreadIds = threadIds;
            if (isPowerHintSessionRunning()) {
                restartPowerHintSession();
            }
        }
    }

    bool startPowerHintSession() override {
        if (mPowerHintSession != nullptr || !mPowerHintTargetDuration.has_value() ||
            mPowerHintThreadIds.empty()) {
            ALOGV("Cannot start power hint session, skipping");
            return false;
        }
        auto ret = mPowerHal->createHintSession(getpid(), static_cast<int32_t>(getuid()),
                                                mPowerHintThreadIds, *mPowerHintTargetDuration,
                                                &mPowerHintSession);
        if (!ret.isOk()) {
            ALOGW("Failed to start power hint session with error: %s",
                  ret.exceptionToString(ret.exceptionCode()).c_str());
            // Indicate to the poweradvisor that this wrapper likely needs to be remade
            mShouldReconnectHal = true;
        }
        return isPowerHintSessionRunning();
    }

    bool shouldSetTargetDuration(int64_t targetDurationNanos) {
        if (!mLastTargetDurationSent.has_value()) {
            return true;
        }

        // report if the change in target from our last submission to now exceeds the threshold
        return abs(1.0 -
                   static_cast<double>(*mLastTargetDurationSent) /
                           static_cast<double>(targetDurationNanos)) >=
                ALLOWED_TARGET_DEVIATION_PERCENT;
    }

    void setTargetWorkDuration(int64_t targetDurationNanos) override {
        mPowerHintTargetDuration = targetDurationNanos;
        if (shouldSetTargetDuration(targetDurationNanos) && isPowerHintSessionRunning()) {
            mLastTargetDurationSent = targetDurationNanos;
            auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos);
            if (!ret.isOk()) {
                ALOGW("Failed to set power hint target work duration with error: %s",
                      ret.exceptionMessage().c_str());
                mShouldReconnectHal = true;
            }
        }
    }

    bool shouldReportActualDurationsNow() {
        // report if we have never reported before or have exceeded the max queue size
        if (!mLastMessageReported.has_value() || mPowerHintQueue.size() >= MAX_QUEUE_SIZE) {
            return true;
        }

        // duration of most recent timing
        const double mostRecentActualDuration =
                static_cast<double>(mPowerHintQueue.back().durationNanos);
        // duration of the last timing actually reported to the powerhal
        const double lastReportedActualDuration =
                static_cast<double>(mLastMessageReported->durationNanos);

        // report if the change in duration from then to now exceeds the threshold
        return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >=
                ALLOWED_ACTUAL_DEVIATION_PERCENT;
    }

    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override {
        if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) {
            ALOGV("Failed to send actual work duration, skipping");
            return;
        }

        WorkDuration duration;
        duration.durationNanos = actualDurationNanos;
        duration.timeStampNanos = timeStampNanos;
        mPowerHintQueue.push_back(duration);

        // This rate limiter queues similar duration reports to the powerhal into
        // batches to avoid excessive binder calls. The criteria to send a given batch
        // are outlined in shouldReportActualDurationsNow()
        if (shouldReportActualDurationsNow()) {
            auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue);
            if (!ret.isOk()) {
                ALOGW("Failed to report actual work durations with error: %s",
                      ret.exceptionMessage().c_str());
                mShouldReconnectHal = true;
            }
            mPowerHintQueue.clear();
            mLastMessageReported = duration;
        }
    }

    bool shouldReconnectHAL() override { return mShouldReconnectHal; }

    std::vector<int32_t> getPowerHintSessionThreadIds() override { return mPowerHintThreadIds; }

    std::optional<int64_t> getTargetWorkDuration() override { return mPowerHintTargetDuration; }

private:
    // max number of messages allowed in mPowerHintQueue before reporting is forced
    static constexpr int32_t MAX_QUEUE_SIZE = 15;
    // max percent the actual duration can vary without causing a report (eg: 0.1 = 10%)
    static constexpr double ALLOWED_ACTUAL_DEVIATION_PERCENT = 0.1;
    // max percent the target duration can vary without causing a report (eg: 0.05 = 5%)
    static constexpr double ALLOWED_TARGET_DEVIATION_PERCENT = 0.05;

    const sp<IPower> mPowerHal = nullptr;
    bool mHasExpensiveRendering = false;
    bool mHasDisplayUpdateImminent = false;
    bool mShouldReconnectHal = false; // used to indicate an error state and need for reconstruction
    // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock
    sp<IPowerHintSession> mPowerHintSession = nullptr;
    std::vector<WorkDuration> mPowerHintQueue;
    // halwrapper owns these values so we can init when we want and reconnect if broken
    std::optional<int64_t> mPowerHintTargetDuration;
    std::vector<int32_t> mPowerHintThreadIds;
    // keep track of the last messages sent for rate limiter change detection
    std::optional<WorkDuration> mLastMessageReported;
    std::optional<int64_t> mLastTargetDurationSent;
    bool mSupportsPowerHints;
};

PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
@@ -246,6 +506,15 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
        return nullptr;
    }

    // grab old hint session values before we destroy any existing wrapper
    std::vector<int32_t> oldPowerHintSessionThreadIds;
    std::optional<int64_t> oldTargetWorkDuration;

    if (sHalWrapper != nullptr) {
        oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds();
        oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration();
    }

    // If we used to have a HAL, but it stopped responding, attempt to reconnect
    if (mReconnectPowerHal) {
        sHalWrapper = nullptr;
@@ -253,15 +522,34 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
    }

    if (sHalWrapper != nullptr) {
        return sHalWrapper.get();
        auto wrapper = sHalWrapper.get();
        // if the wrapper is fine, return it, but if it indicates a reconnect, remake it
        if (!wrapper->shouldReconnectHAL()) {
            return wrapper;
        }
        sHalWrapper = nullptr;
    }

    // at this point, we know for sure there is no running session
    mPowerHintSessionRunning = false;

    // First attempt to connect to the AIDL Power HAL
    sHalWrapper = AidlPowerHalWrapper::connect();

    // If that didn't succeed, attempt to connect to the HIDL Power HAL
    if (sHalWrapper == nullptr) {
        sHalWrapper = HidlPowerHalWrapper::connect();
    } else { // if AIDL, pass on any existing hint session values
        // thread ids always safe to set
        sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds);
        // only set duration and start if duration is defined
        if (oldTargetWorkDuration.has_value()) {
            sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration);
            // only start if possible to run and both threadids and duration are defined
            if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) {
                mPowerHintSessionRunning = sHalWrapper->startPowerHintSession();
            }
        }
    }

    // If we make it to this point and still don't have a HAL, it's unlikely we
+29 −1
Original line number Diff line number Diff line
@@ -40,6 +40,13 @@ public:
    virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
    virtual bool isUsingExpensiveRendering() = 0;
    virtual void notifyDisplayUpdateImminent() = 0;
    virtual bool usePowerHintSession() = 0;
    virtual bool supportsPowerHintSession() = 0;
    virtual bool isPowerHintSessionRunning() = 0;
    virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
    virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
    virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0;
    virtual void enablePowerHint(bool enabled) = 0;
};

namespace impl {
@@ -54,6 +61,17 @@ public:

        virtual bool setExpensiveRendering(bool enabled) = 0;
        virtual bool notifyDisplayUpdateImminent() = 0;
        virtual bool supportsPowerHintSession() = 0;
        virtual bool isPowerHintSessionRunning() = 0;
        virtual void restartPowerHintSession() = 0;
        virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0;
        virtual bool startPowerHintSession() = 0;
        virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0;
        virtual void sendActualWorkDuration(int64_t actualDurationNanos,
                                            nsecs_t timeStampNanos) = 0;
        virtual bool shouldReconnectHAL() = 0;
        virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0;
        virtual std::optional<int64_t> getTargetWorkDuration() = 0;
    };

    PowerAdvisor(SurfaceFlinger& flinger);
@@ -62,8 +80,15 @@ public:
    void init() override;
    void onBootFinished() override;
    void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
    bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; }
    bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
    void notifyDisplayUpdateImminent() override;
    bool usePowerHintSession() override;
    bool supportsPowerHintSession() override;
    bool isPowerHintSessionRunning() override;
    void setTargetWorkDuration(int64_t targetDurationNanos) override;
    void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override;
    void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override;
    void enablePowerHint(bool enabled) override;

private:
    HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
@@ -71,6 +96,9 @@ private:
    std::mutex mPowerHalMutex;

    std::atomic_bool mBootFinished = false;
    std::optional<bool> mPowerHintEnabled;
    std::optional<bool> mSupportsPowerHint;
    bool mPowerHintSessionRunning = false;

    std::unordered_set<DisplayId> mExpensiveDisplays;
    bool mNotifiedExpensiveRendering = false;
+1 −0
Original line number Diff line number Diff line
@@ -709,6 +709,7 @@ void SurfaceFlinger::bootFinished() {
    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );

    mFlagManager = std::make_unique<android::FlagManager>();
    mPowerAdvisor.enablePowerHint(mFlagManager->use_adpf_cpu_hint());
    mFrameTracer->initialize();
    mFrameTimeline->onBootFinished();

Loading