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

Commit 57274054 authored by Matt Buckley's avatar Matt Buckley
Browse files

Add additional tests for PowerAdvisor

Adds additional tests for PowerAdvisor functionality regarding
timing and multidisplay

Bug: b/241465879
Test: atest
libsurfaceflinger_unittest:libsurfaceflinger_unittest.PowerAdvisorTest

Change-Id: I833e91efb5608d5972220c679a061c9bb51372fa
parent 0d4ac562
Loading
Loading
Loading
Loading
+17 −20
Original line number Diff line number Diff line
@@ -838,10 +838,7 @@ const bool AidlPowerHalWrapper::sTraceHintSessionData =
        base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false);

PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
    static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
    static bool sHasHal = true;

    if (!sHasHal) {
    if (!mHasHal) {
        return nullptr;
    }

@@ -849,57 +846,57 @@ PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
    std::vector<int32_t> oldPowerHintSessionThreadIds;
    std::optional<int64_t> oldTargetWorkDuration;

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

    // If we used to have a HAL, but it stopped responding, attempt to reconnect
    if (mReconnectPowerHal) {
        sHalWrapper = nullptr;
        mHalWrapper = nullptr;
        mReconnectPowerHal = false;
    }

    if (sHalWrapper != nullptr) {
        auto wrapper = sHalWrapper.get();
    if (mHalWrapper != nullptr) {
        auto wrapper = mHalWrapper.get();
        // If the wrapper is fine, return it, but if it indicates a reconnect, remake it
        if (!wrapper->shouldReconnectHAL()) {
            return wrapper;
        }
        ALOGD("Reconnecting Power HAL");
        sHalWrapper = nullptr;
        mHalWrapper = 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();
    mHalWrapper = AidlPowerHalWrapper::connect();

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

    // If we make it to this point and still don't have a HAL, it's unlikely we
    // will, so stop trying
    if (sHalWrapper == nullptr) {
        sHasHal = false;
    if (mHalWrapper == nullptr) {
        mHasHal = false;
    }

    return sHalWrapper.get();
    return mHalWrapper.get();
}

} // namespace impl
+7 −0
Original line number Diff line number Diff line
@@ -154,6 +154,13 @@ public:
    void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override;

private:
    friend class PowerAdvisorTest;

    // Tracks if powerhal exists
    bool mHasHal = true;
    // Holds the hal wrapper for getPowerHal
    std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr;

    HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex);
    bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false;
    std::mutex mPowerHalMutex;
+2 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ package {
filegroup {
    name: "libsurfaceflinger_mock_sources",
    srcs: [
        "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp",
        "mock/DisplayHardware/MockComposer.cpp",
        "mock/DisplayHardware/MockHWC2.cpp",
        "mock/DisplayHardware/MockIPower.cpp",
@@ -95,6 +96,7 @@ cc_test {
        "LayerTest.cpp",
        "LayerTestUtils.cpp",
        "MessageQueueTest.cpp",
        "PowerAdvisorTest.cpp",
        "SurfaceFlinger_CreateDisplayTest.cpp",
        "SurfaceFlinger_DestroyDisplayTest.cpp",
        "SurfaceFlinger_DisplayModeSwitching.cpp",
+203 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#undef LOG_TAG
#define LOG_TAG "PowerAdvisorTest"

#include <DisplayHardware/PowerAdvisor.h>
#include <compositionengine/Display.h>
#include <ftl/fake_guard.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <ui/DisplayId.h>
#include <chrono>
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h"

using namespace android;
using namespace android::Hwc2::mock;
using namespace android::hardware::power;
using namespace std::chrono_literals;
using namespace testing;

namespace android::Hwc2::impl {

class PowerAdvisorTest : public testing::Test {
public:
    void SetUp() override;
    void startPowerHintSession();
    void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
    void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod);
    nsecs_t getFenceWaitDelayDuration(bool skipValidate);

protected:
    TestableSurfaceFlinger mFlinger;
    std::unique_ptr<PowerAdvisor> mPowerAdvisor;
    NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper;
    nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count();
};

void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) {
    std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper =
            std::make_unique<NiceMock<MockAidlPowerHalWrapper>>();
    mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger());
    ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true));
    ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true));
    mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper);
    mMockAidlWrapper =
            reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get());
}

void PowerAdvisorTest::startPowerHintSession() {
    const std::vector<int32_t> threadIds = {1, 2, 3};
    mPowerAdvisor->enablePowerHint(true);
    mPowerAdvisor->startPowerHintSession(threadIds);
}

void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) {
    mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget);
    mPowerAdvisor->setExpectedPresentTime(expectedPresentTime);
}

void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) {
    mPowerAdvisor->setCommitStart(startTime);
    mPowerAdvisor->setFrameDelay(0);
    mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
}

nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
    return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
                         : PowerAdvisor::kFenceWaitStartDelayValidated)
            .count();
}

namespace {

TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) {
    mPowerAdvisor->onBootFinished();
    startPowerHintSession();

    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};

    // 60hz
    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();

    nsecs_t startTime = 100;

    // advisor only starts on frame 2 so do an initial no-op frame
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);

    // increment the frame
    startTime += vsyncPeriod;

    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);

    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->sendActualWorkDuration();
}

TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) {
    mPowerAdvisor->onBootFinished();
    startPowerHintSession();

    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)};

    // 60hz
    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
    const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count();
    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();
    const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count();

    nsecs_t startTime = 100;

    // advisor only starts on frame 2 so do an initial no-op frame
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);

    // increment the frame
    startTime += vsyncPeriod;

    const nsecs_t expectedDuration = kErrorMargin + presentDuration +
            getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration;
    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);

    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000);
    // now report the fence as having fired during the display HWC time
    mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration,
                                      startTime + presentDuration);
    mPowerAdvisor->sendActualWorkDuration();
}

TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) {
    mPowerAdvisor->onBootFinished();
    startPowerHintSession();

    std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
                                      GpuVirtualDisplayId(1)};

    // 60hz
    const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60;
    // make present duration much later than the hwc display by itself will account for
    const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count();
    const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count();

    nsecs_t startTime = 100;

    // advisor only starts on frame 2 so do an initial no-op frame
    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration);

    // increment the frame
    startTime += vsyncPeriod;

    const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration;
    EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1);

    fakeBasicFrameTiming(startTime, vsyncPeriod);
    setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
    mPowerAdvisor->setDisplays(displayIds);

    // don't report timing for the gpu displays since they don't use hwc
    mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000);
    mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000);
    mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration);
    mPowerAdvisor->sendActualWorkDuration();
}

} // namespace
} // namespace android::Hwc2::impl
+26 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "MockAidlPowerHalWrapper.h"
#include "MockIPower.h"

namespace android::Hwc2::mock {

MockAidlPowerHalWrapper::MockAidlPowerHalWrapper()
      : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){};
MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default;

} // namespace android::Hwc2::mock
Loading