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

Commit 1d7a3086 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "SF: add partial implementation of VSyncReactor"

parents 080cd5a9 b2501bad
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -174,6 +174,7 @@ filegroup {
        "Scheduler/VSyncDispatchTimerQueue.cpp",
        "Scheduler/VSyncPredictor.cpp",
        "Scheduler/VSyncModulator.cpp",
        "Scheduler/VSyncReactor.cpp",
        "StartPropertySetThread.cpp",
        "SurfaceFlinger.cpp",
        "SurfaceFlingerDefaultFactory.cpp",
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 "VSyncReactor.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"

namespace android::scheduler {

VSyncReactor::VSyncReactor(std::unique_ptr<VSyncDispatch> dispatch,
                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
      : mDispatch(std::move(dispatch)),
        mTracker(std::move(tracker)),
        mPendingLimit(pendingFenceLimit) {}

bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
    if (!fence) {
        return false;
    }

    nsecs_t const signalTime = fence->getCachedSignalTime();
    if (signalTime == Fence::SIGNAL_TIME_INVALID) {
        return true;
    }

    std::lock_guard<std::mutex> lk(mMutex);
    if (mIgnorePresentFences) {
        return true;
    }

    for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
        auto const time = (*it)->getCachedSignalTime();
        if (time == Fence::SIGNAL_TIME_PENDING) {
            it++;
        } else if (time == Fence::SIGNAL_TIME_INVALID) {
            it = mUnfiredFences.erase(it);
        } else {
            mTracker->addVsyncTimestamp(time);
            it = mUnfiredFences.erase(it);
        }
    }

    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
        if (mPendingLimit == mUnfiredFences.size()) {
            mUnfiredFences.erase(mUnfiredFences.begin());
        }
        mUnfiredFences.push_back(fence);
    } else {
        mTracker->addVsyncTimestamp(signalTime);
    }

    return false; // TODO(b/144707443): add policy for turning on HWVsync.
}

void VSyncReactor::setIgnorePresentFences(bool ignoration) {
    std::lock_guard<std::mutex> lk(mMutex);
    mIgnorePresentFences = ignoration;
    if (mIgnorePresentFences == true) {
        mUnfiredFences.clear();
    }
}

} // namespace android::scheduler
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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.
 */

#pragma once

#include <android-base/thread_annotations.h>
#include <ui/FenceTime.h>
#include <memory>
#include <mutex>
#include <vector>

namespace android::scheduler {

class VSyncDispatch;
class VSyncTracker;

// TODO (b/145217110): consider renaming.
class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
public:
    VSyncReactor(std::unique_ptr<VSyncDispatch> dispatch, std::unique_ptr<VSyncTracker> tracker,
                 size_t pendingFenceLimit);

    bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
    void setIgnorePresentFences(bool ignoration);

private:
    std::unique_ptr<VSyncDispatch> const mDispatch;
    std::unique_ptr<VSyncTracker> const mTracker;
    size_t const mPendingLimit;

    std::mutex mMutex;
    bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
};

} // namespace android::scheduler
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ cc_test {
        "VSyncDispatchTimerQueueTest.cpp",
        "VSyncDispatchRealtimeTest.cpp",
        "VSyncPredictorTest.cpp",
        "VSyncReactorTest.cpp",
        "mock/DisplayHardware/MockComposer.cpp",
        "mock/DisplayHardware/MockDisplay.cpp",
        "mock/DisplayHardware/MockPowerAdvisor.cpp",
+184 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 "LibSurfaceFlingerUnittests"
#define LOG_NDEBUG 0

#include "Scheduler/VSyncDispatch.h"
#include "Scheduler/VSyncReactor.h"
#include "Scheduler/VSyncTracker.h"

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <ui/Fence.h>
#include <ui/FenceTime.h>
#include <array>

using namespace testing;
using namespace std::literals;
namespace android::scheduler {

class MockVSyncTracker : public VSyncTracker {
public:
    MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
};

class VSyncTrackerWrapper : public VSyncTracker {
public:
    VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}

    void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
        return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
    }

private:
    std::shared_ptr<VSyncTracker> const mTracker;
};

class MockVSyncDispatch : public VSyncDispatch {
public:
    MOCK_METHOD2(registerCallback, CallbackToken(std::function<void(nsecs_t)> const&, std::string));
    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
    MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
    MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
};

class VSyncDispatchWrapper : public VSyncDispatch {
public:
    VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
    CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
                                   std::string callbackName) final {
        return mDispatch->registerCallback(callbackFn, callbackName);
    }

    void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }

    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
                            nsecs_t earliestVsync) final {
        return mDispatch->schedule(token, workDuration, earliestVsync);
    }

    CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }

private:
    std::shared_ptr<VSyncDispatch> const mDispatch;
};

std::shared_ptr<FenceTime> generateInvalidFence() {
    sp<Fence> fence = new Fence();
    return std::make_shared<FenceTime>(fence);
}

std::shared_ptr<FenceTime> generatePendingFence() {
    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
    return std::make_shared<FenceTime>(fence);
}

void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
    FenceTime::Snapshot snap(time);
    fence->applyTrustedSnapshot(snap);
}

std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
    signalFenceWithTime(ft, time);
    return ft;
}

class VSyncReactorTest : public testing::Test {
protected:
    VSyncReactorTest()
          : mMockDispatch(std::make_shared<MockVSyncDispatch>()),
            mMockTracker(std::make_shared<MockVSyncTracker>()),
            mReactor(std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {}

    std::shared_ptr<MockVSyncDispatch> mMockDispatch;
    std::shared_ptr<MockVSyncTracker> mMockTracker;
    static constexpr size_t kPendingLimit = 3;
    static constexpr nsecs_t dummyTime = 47;
    VSyncReactor mReactor;
};

TEST_F(VSyncReactorTest, addingNullFenceCheck) {
    EXPECT_FALSE(mReactor.addPresentFence(nullptr));
}

TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
    EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
}

TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
}

TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
    nsecs_t anotherDummyTime = 2919019201;

    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
    auto pendingFence = generatePendingFence();
    EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
    Mock::VerifyAndClearExpectations(mMockTracker.get());

    signalFenceWithTime(pendingFence, dummyTime);

    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
}

TEST_F(VSyncReactorTest, limitsPendingFences) {
    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
    std::array<nsecs_t, fences.size()> fakeTimes;
    std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
    std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
        i++;
        return i * i;
    });

    for (auto const& fence : fences) {
        mReactor.addPresentFence(fence);
    }

    for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
        EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
    }

    for (auto i = 0u; i < fences.size(); i++) {
        signalFenceWithTime(fences[i], fakeTimes[i]);
    }
    mReactor.addPresentFence(generatePendingFence());
}

TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
    static constexpr size_t aFewTimes = 8;
    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);

    mReactor.setIgnorePresentFences(true);
    for (auto i = 0; i < aFewTimes; i++) {
        mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
    }

    mReactor.setIgnorePresentFences(false);
    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
}

} // namespace android::scheduler