You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1219 lines
45 KiB
1219 lines
45 KiB
/*
|
|
* 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.
|
|
*/
|
|
|
|
// TODO(b/129481165): remove the #pragma below and fix conversion issues
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wconversion"
|
|
#pragma clang diagnostic ignored "-Wextra"
|
|
|
|
#undef LOG_TAG
|
|
#define LOG_TAG "LibSurfaceFlingerUnittests"
|
|
#define LOG_NDEBUG 0
|
|
|
|
#include "Scheduler/TimeKeeper.h"
|
|
#include "Scheduler/VSyncDispatchTimerQueue.h"
|
|
#include "Scheduler/VSyncTracker.h"
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
#include <thread>
|
|
|
|
using namespace testing;
|
|
using namespace std::literals;
|
|
namespace android::scheduler {
|
|
|
|
class MockVSyncTracker : public VSyncTracker {
|
|
public:
|
|
MockVSyncTracker(nsecs_t period) : mPeriod{period} {
|
|
ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
|
|
.WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
|
|
ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
|
|
}
|
|
|
|
MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
|
|
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
|
|
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
|
|
MOCK_METHOD1(setPeriod, void(nsecs_t));
|
|
MOCK_METHOD0(resetModel, void());
|
|
MOCK_CONST_METHOD0(needsMoreSamples, bool());
|
|
MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps));
|
|
MOCK_CONST_METHOD1(dump, void(std::string&));
|
|
|
|
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
|
|
if (timePoint % mPeriod == 0) {
|
|
return timePoint;
|
|
}
|
|
return (timePoint - (timePoint % mPeriod) + mPeriod);
|
|
}
|
|
|
|
protected:
|
|
nsecs_t const mPeriod;
|
|
};
|
|
|
|
class ControllableClock : public TimeKeeper {
|
|
public:
|
|
ControllableClock() {
|
|
ON_CALL(*this, alarmAt(_, _))
|
|
.WillByDefault(Invoke(this, &ControllableClock::alarmAtDefaultBehavior));
|
|
ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
|
|
}
|
|
|
|
MOCK_CONST_METHOD0(now, nsecs_t());
|
|
MOCK_METHOD2(alarmAt, void(std::function<void()> const&, nsecs_t time));
|
|
MOCK_METHOD0(alarmCancel, void());
|
|
MOCK_CONST_METHOD1(dump, void(std::string&));
|
|
|
|
void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
|
|
mCallback = callback;
|
|
mNextCallbackTime = time;
|
|
}
|
|
|
|
nsecs_t fakeTime() const { return mCurrentTime; }
|
|
|
|
void advanceToNextCallback() {
|
|
mCurrentTime = mNextCallbackTime;
|
|
if (mCallback) {
|
|
mCallback();
|
|
}
|
|
}
|
|
|
|
void advanceBy(nsecs_t advancement) {
|
|
mCurrentTime += advancement;
|
|
if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) {
|
|
mCallback();
|
|
}
|
|
};
|
|
|
|
void setLag(nsecs_t lag) { mLag = lag; }
|
|
|
|
private:
|
|
std::function<void()> mCallback;
|
|
nsecs_t mNextCallbackTime = 0;
|
|
nsecs_t mCurrentTime = 0;
|
|
nsecs_t mLag = 0;
|
|
};
|
|
|
|
class CountingCallback {
|
|
public:
|
|
CountingCallback(VSyncDispatch& dispatch)
|
|
: mDispatch(dispatch),
|
|
mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
|
|
std::placeholders::_1, std::placeholders::_2,
|
|
std::placeholders::_3),
|
|
"test")) {}
|
|
~CountingCallback() { mDispatch.unregisterCallback(mToken); }
|
|
|
|
operator VSyncDispatch::CallbackToken() const { return mToken; }
|
|
|
|
void counter(nsecs_t time, nsecs_t wakeup_time, nsecs_t readyTime) {
|
|
mCalls.push_back(time);
|
|
mWakeupTime.push_back(wakeup_time);
|
|
mReadyTime.push_back(readyTime);
|
|
}
|
|
|
|
VSyncDispatch& mDispatch;
|
|
VSyncDispatch::CallbackToken mToken;
|
|
std::vector<nsecs_t> mCalls;
|
|
std::vector<nsecs_t> mWakeupTime;
|
|
std::vector<nsecs_t> mReadyTime;
|
|
};
|
|
|
|
class PausingCallback {
|
|
public:
|
|
PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
|
|
: mDispatch(dispatch),
|
|
mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
|
|
std::placeholders::_1,
|
|
std::placeholders::_2),
|
|
"test")),
|
|
mRegistered(true),
|
|
mPauseAmount(pauseAmount) {}
|
|
~PausingCallback() { unregister(); }
|
|
|
|
operator VSyncDispatch::CallbackToken() const { return mToken; }
|
|
|
|
void pause(nsecs_t, nsecs_t) {
|
|
std::unique_lock lock(mMutex);
|
|
mPause = true;
|
|
mCv.notify_all();
|
|
|
|
mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; });
|
|
|
|
mResourcePresent = (mResource.lock() != nullptr);
|
|
}
|
|
|
|
bool waitForPause() {
|
|
std::unique_lock lock(mMutex);
|
|
auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; });
|
|
return waiting;
|
|
}
|
|
|
|
void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; }
|
|
|
|
bool resourcePresent() { return mResourcePresent; }
|
|
|
|
void unpause() {
|
|
std::unique_lock lock(mMutex);
|
|
mPause = false;
|
|
mCv.notify_all();
|
|
}
|
|
|
|
void unregister() {
|
|
if (mRegistered) {
|
|
mDispatch.unregisterCallback(mToken);
|
|
mRegistered = false;
|
|
}
|
|
}
|
|
|
|
VSyncDispatch& mDispatch;
|
|
VSyncDispatch::CallbackToken mToken;
|
|
bool mRegistered = true;
|
|
|
|
std::mutex mMutex;
|
|
std::condition_variable mCv;
|
|
bool mPause = false;
|
|
std::weak_ptr<void> mResource;
|
|
bool mResourcePresent = false;
|
|
std::chrono::milliseconds const mPauseAmount;
|
|
};
|
|
|
|
class VSyncDispatchTimerQueueTest : public testing::Test {
|
|
protected:
|
|
std::unique_ptr<TimeKeeper> createTimeKeeper() {
|
|
class TimeKeeperWrapper : public TimeKeeper {
|
|
public:
|
|
TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
|
|
void alarmAt(std::function<void()> const& callback, nsecs_t time) final {
|
|
mControllableClock.alarmAt(callback, time);
|
|
}
|
|
void alarmCancel() final { mControllableClock.alarmCancel(); }
|
|
nsecs_t now() const final { return mControllableClock.now(); }
|
|
void dump(std::string&) const final {}
|
|
|
|
private:
|
|
TimeKeeper& mControllableClock;
|
|
};
|
|
return std::make_unique<TimeKeeperWrapper>(mMockClock);
|
|
}
|
|
|
|
~VSyncDispatchTimerQueueTest() {
|
|
// destructor of dispatch will cancelAlarm(). Ignore final cancel in common test.
|
|
Mock::VerifyAndClearExpectations(&mMockClock);
|
|
}
|
|
|
|
void advanceToNextCallback() { mMockClock.advanceToNextCallback(); }
|
|
|
|
NiceMock<ControllableClock> mMockClock;
|
|
static nsecs_t constexpr mDispatchGroupThreshold = 5;
|
|
nsecs_t const mPeriod = 1000;
|
|
nsecs_t const mVsyncMoveThreshold = 300;
|
|
NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
|
|
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
|
|
mVsyncMoveThreshold};
|
|
};
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
EXPECT_CALL(mMockClock, alarmCancel());
|
|
{
|
|
VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
|
|
mVsyncMoveThreshold};
|
|
CountingCallback cb(mDispatch);
|
|
const auto result = mDispatch.schedule(cb,
|
|
{.workDuration = 100,
|
|
.readyDuration = 0,
|
|
.earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(900, *result);
|
|
}
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
|
|
auto intended = mPeriod - 230;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
|
|
CountingCallback cb(mDispatch);
|
|
const auto result = mDispatch.schedule(cb,
|
|
{.workDuration = 100,
|
|
.readyDuration = 0,
|
|
.earliestVsync = intended});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(900, *result);
|
|
|
|
advanceToNextCallback();
|
|
|
|
ASSERT_THAT(cb.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1050));
|
|
|
|
CountingCallback cb(mDispatch);
|
|
mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
advanceToNextCallback();
|
|
|
|
ASSERT_THAT(cb.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb.mCalls[0], Eq(1150));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) {
|
|
auto const now = 234;
|
|
mMockClock.advanceBy(234);
|
|
auto const workDuration = 10 * mPeriod;
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
|
|
.WillOnce(Return(mPeriod * 11));
|
|
EXPECT_CALL(mMockClock, alarmAt(_, mPeriod));
|
|
|
|
CountingCallback cb(mDispatch);
|
|
const auto result = mDispatch.schedule(cb,
|
|
{.workDuration = workDuration,
|
|
.readyDuration = 0,
|
|
.earliestVsync = mPeriod});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod, *result);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
EXPECT_CALL(mMockClock, alarmCancel());
|
|
|
|
CountingCallback cb(mDispatch);
|
|
const auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod - 100, *result);
|
|
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
EXPECT_CALL(mMockClock, alarmCancel());
|
|
|
|
CountingCallback cb(mDispatch);
|
|
const auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod - 100, *result);
|
|
mMockClock.advanceBy(950);
|
|
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
EXPECT_CALL(mMockClock, alarmCancel());
|
|
|
|
PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
|
|
const auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod - 100, *result);
|
|
|
|
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
|
|
EXPECT_TRUE(cb.waitForPause());
|
|
EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
|
|
cb.unpause();
|
|
pausingThread.join();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
EXPECT_CALL(mMockClock, alarmCancel());
|
|
|
|
auto resource = std::make_shared<int>(110);
|
|
|
|
PausingCallback cb(mDispatch, 50ms);
|
|
cb.stashResource(resource);
|
|
const auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod - 100, *result);
|
|
|
|
std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
|
|
EXPECT_TRUE(cb.waitForPause());
|
|
|
|
cb.unregister();
|
|
resource.reset();
|
|
|
|
cb.unpause();
|
|
pausingThread.join();
|
|
|
|
EXPECT_TRUE(cb.resourcePresent());
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
|
|
.Times(4)
|
|
.WillOnce(Return(1055))
|
|
.WillOnce(Return(1063))
|
|
.WillOnce(Return(1063))
|
|
.WillOnce(Return(1075));
|
|
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 955)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 813)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 975)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
|
|
advanceToNextCallback();
|
|
advanceToNextCallback();
|
|
|
|
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb0.mCalls[0], Eq(1075));
|
|
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb1.mCalls[0], Eq(1063));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
|
|
.Times(4)
|
|
.WillOnce(Return(10000))
|
|
.WillOnce(Return(1000))
|
|
.WillOnce(Return(10000))
|
|
.WillOnce(Return(10000));
|
|
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 750)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 9900)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10});
|
|
mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod});
|
|
mDispatch.cancel(cb1);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000});
|
|
advanceToNextCallback();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
advanceToNextCallback();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1590)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1600)).InSequence(seq);
|
|
|
|
auto offset = 400;
|
|
auto closeOffset = offset + mDispatchGroupThreshold - 1;
|
|
auto notCloseOffset = offset + 2 * mDispatchGroupThreshold;
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1,
|
|
{.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000});
|
|
|
|
advanceToNextCallback();
|
|
ASSERT_THAT(cb0.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod));
|
|
ASSERT_THAT(cb1.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000});
|
|
mDispatch.schedule(cb1,
|
|
{.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000});
|
|
advanceToNextCallback();
|
|
ASSERT_THAT(cb1.mCalls.size(), Eq(2));
|
|
EXPECT_THAT(cb1.mCalls[1], Eq(2000));
|
|
|
|
advanceToNextCallback();
|
|
ASSERT_THAT(cb0.mCalls.size(), Eq(2));
|
|
EXPECT_THAT(cb0.mCalls[1], Eq(2000));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 800)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmCancel());
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000});
|
|
advanceToNextCallback();
|
|
EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
|
|
.Times(3)
|
|
.WillOnce(Return(950))
|
|
.WillOnce(Return(1975))
|
|
.WillOnce(Return(2950));
|
|
|
|
CountingCallback cb(mDispatch);
|
|
mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920});
|
|
|
|
mMockClock.advanceBy(850);
|
|
EXPECT_THAT(cb.mCalls.size(), Eq(1));
|
|
|
|
mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900});
|
|
mMockClock.advanceBy(900);
|
|
EXPECT_THAT(cb.mCalls.size(), Eq(1));
|
|
mMockClock.advanceBy(125);
|
|
EXPECT_THAT(cb.mCalls.size(), Eq(2));
|
|
|
|
mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900});
|
|
mMockClock.advanceBy(975);
|
|
EXPECT_THAT(cb.mCalls.size(), Eq(3));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
|
|
|
|
VSyncDispatch::CallbackToken tmp;
|
|
tmp = mDispatch.registerCallback(
|
|
[&](auto, auto, auto) {
|
|
mDispatch.schedule(tmp,
|
|
{.workDuration = 100,
|
|
.readyDuration = 0,
|
|
.earliestVsync = 2000});
|
|
},
|
|
"o.o");
|
|
|
|
mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
advanceToNextCallback();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
|
|
VSyncDispatch::CallbackToken tmp;
|
|
std::optional<nsecs_t> lastTarget;
|
|
tmp = mDispatch.registerCallback(
|
|
[&](auto timestamp, auto, auto) {
|
|
auto result =
|
|
mDispatch.schedule(tmp,
|
|
{.workDuration = 400,
|
|
.readyDuration = 0,
|
|
.earliestVsync = timestamp - mVsyncMoveThreshold});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod + timestamp - 400, *result);
|
|
result = mDispatch.schedule(tmp,
|
|
{.workDuration = 400,
|
|
.readyDuration = 0,
|
|
.earliestVsync = timestamp});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod + timestamp - 400, *result);
|
|
result = mDispatch.schedule(tmp,
|
|
{.workDuration = 400,
|
|
.readyDuration = 0,
|
|
.earliestVsync = timestamp + mVsyncMoveThreshold});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(mPeriod + timestamp - 400, *result);
|
|
lastTarget = timestamp;
|
|
},
|
|
"oo");
|
|
|
|
mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000});
|
|
advanceToNextCallback();
|
|
EXPECT_THAT(lastTarget, Eq(1000));
|
|
|
|
advanceToNextCallback();
|
|
EXPECT_THAT(lastTarget, Eq(2000));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1000)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 950)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1950)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
|
|
|
|
CountingCallback cb(mDispatch);
|
|
mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000});
|
|
|
|
mMockClock.advanceBy(750);
|
|
mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000});
|
|
|
|
advanceToNextCallback();
|
|
mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000});
|
|
|
|
mMockClock.advanceBy(800);
|
|
mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 850)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1800)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
|
|
mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
|
|
advanceToNextCallback();
|
|
mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000});
|
|
mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000});
|
|
|
|
advanceToNextCallback();
|
|
advanceToNextCallback();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
CountingCallback cb1(mDispatch);
|
|
mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000});
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq);
|
|
|
|
CountingCallback cb0(mDispatch);
|
|
mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.cancel(cb0);
|
|
mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
|
|
VSyncDispatch::CallbackToken token(100);
|
|
EXPECT_FALSE(mDispatch
|
|
.schedule(token,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000})
|
|
.has_value());
|
|
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
|
|
CountingCallback cb0(mDispatch);
|
|
auto result =
|
|
mDispatch.schedule(cb0,
|
|
{.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(500, *result);
|
|
result = mDispatch.schedule(cb0,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(900, *result);
|
|
}
|
|
|
|
// b/1450138150
|
|
TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500));
|
|
CountingCallback cb(mDispatch);
|
|
auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(500, *result);
|
|
mMockClock.advanceBy(400);
|
|
|
|
result = mDispatch.schedule(cb,
|
|
{.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(1200, *result);
|
|
advanceToNextCallback();
|
|
ASSERT_THAT(cb.mCalls.size(), Eq(1));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
|
|
.Times(2)
|
|
.WillOnce(Return(1000))
|
|
.WillOnce(Return(1002));
|
|
CountingCallback cb(mDispatch);
|
|
auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(500, *result);
|
|
mMockClock.advanceBy(400);
|
|
result = mDispatch.schedule(cb,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(602, *result);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
|
|
CountingCallback cb0(mDispatch);
|
|
auto result =
|
|
mDispatch.schedule(cb0,
|
|
{.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(500, *result);
|
|
advanceToNextCallback();
|
|
result = mDispatch.schedule(cb0,
|
|
{.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(900, *result);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq);
|
|
CountingCallback cb0(mDispatch);
|
|
auto result =
|
|
mDispatch.schedule(cb0,
|
|
{.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(500, *result);
|
|
advanceToNextCallback();
|
|
result = mDispatch.schedule(cb0,
|
|
{.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(1100, *result);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600));
|
|
|
|
CountingCallback cb(mDispatch);
|
|
auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
|
|
result = mDispatch.schedule(cb,
|
|
{.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
|
|
advanceToNextCallback();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
|
|
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
|
|
|
|
VSyncCallbackRegistration cb(
|
|
mDispatch, [](auto, auto, auto) {}, "");
|
|
VSyncCallbackRegistration cb1(std::move(cb));
|
|
cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
cb.cancel();
|
|
|
|
cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
cb1.cancel();
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 500)).Times(1);
|
|
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
|
|
|
|
VSyncCallbackRegistration cb(
|
|
mDispatch, [](auto, auto, auto) {}, "");
|
|
VSyncCallbackRegistration cb1(
|
|
mDispatch, [](auto, auto, auto) {}, "");
|
|
cb1 = std::move(cb);
|
|
cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000});
|
|
cb.cancel();
|
|
|
|
cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000});
|
|
cb1.cancel();
|
|
}
|
|
|
|
// b/154303580
|
|
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
|
|
CountingCallback cb1(mDispatch);
|
|
CountingCallback cb2(mDispatch);
|
|
|
|
auto result =
|
|
mDispatch.schedule(cb1,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
|
|
mMockClock.setLag(100);
|
|
mMockClock.advanceBy(620);
|
|
|
|
result = mDispatch.schedule(cb2,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(1900, *result);
|
|
mMockClock.advanceBy(80);
|
|
|
|
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb2.mCalls.size(), Eq(0));
|
|
}
|
|
|
|
// b/154303580.
|
|
// If the same callback tries to reschedule itself after it's too late, timer opts to apply the
|
|
// update later, as opposed to blocking the calling thread.
|
|
TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminentSameCallback) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq);
|
|
CountingCallback cb(mDispatch);
|
|
|
|
auto result =
|
|
mDispatch.schedule(cb,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
|
|
mMockClock.setLag(100);
|
|
mMockClock.advanceBy(620);
|
|
|
|
result = mDispatch.schedule(cb,
|
|
{.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(1630, *result);
|
|
mMockClock.advanceBy(80);
|
|
|
|
EXPECT_THAT(cb.mCalls.size(), Eq(1));
|
|
}
|
|
|
|
// b/154303580.
|
|
TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
|
|
CountingCallback cb1(mDispatch);
|
|
CountingCallback cb2(mDispatch);
|
|
|
|
auto result =
|
|
mDispatch.schedule(cb1,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
result = mDispatch.schedule(cb2,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(1900, *result);
|
|
|
|
mMockClock.setLag(100);
|
|
mMockClock.advanceBy(620);
|
|
|
|
EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled);
|
|
|
|
mMockClock.advanceBy(80);
|
|
|
|
EXPECT_THAT(cb1.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb2.mCalls.size(), Eq(0));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq);
|
|
EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
|
|
CountingCallback cb1(mDispatch);
|
|
CountingCallback cb2(mDispatch);
|
|
|
|
auto result =
|
|
mDispatch.schedule(cb1,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
result = mDispatch.schedule(cb2,
|
|
{.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(1900, *result);
|
|
|
|
mMockClock.setLag(100);
|
|
mMockClock.advanceBy(620);
|
|
|
|
EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled);
|
|
|
|
EXPECT_THAT(cb1.mCalls.size(), Eq(0));
|
|
EXPECT_THAT(cb2.mCalls.size(), Eq(0));
|
|
mMockClock.advanceToNextCallback();
|
|
|
|
EXPECT_THAT(cb1.mCalls.size(), Eq(0));
|
|
EXPECT_THAT(cb2.mCalls.size(), Eq(1));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) {
|
|
CountingCallback cb1(mDispatch);
|
|
CountingCallback cb2(mDispatch);
|
|
|
|
Sequence seq;
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
|
|
.InSequence(seq)
|
|
.WillOnce(Return(1000));
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
|
|
.InSequence(seq)
|
|
.WillOnce(Return(1000));
|
|
|
|
auto result =
|
|
mDispatch.schedule(cb1,
|
|
{.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(600, *result);
|
|
result = mDispatch.schedule(cb2,
|
|
{.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(610, *result);
|
|
|
|
mMockClock.setLag(100);
|
|
mMockClock.advanceBy(700);
|
|
|
|
ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1));
|
|
EXPECT_THAT(cb1.mWakeupTime[0], Eq(600));
|
|
ASSERT_THAT(cb1.mReadyTime.size(), Eq(1));
|
|
EXPECT_THAT(cb1.mReadyTime[0], Eq(1000));
|
|
ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1));
|
|
EXPECT_THAT(cb2.mWakeupTime[0], Eq(610));
|
|
ASSERT_THAT(cb2.mReadyTime.size(), Eq(1));
|
|
EXPECT_THAT(cb2.mReadyTime[0], Eq(1000));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) {
|
|
auto intended = mPeriod - 230;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 900));
|
|
|
|
CountingCallback cb(mDispatch);
|
|
const auto result = mDispatch.schedule(cb,
|
|
{.workDuration = 70,
|
|
.readyDuration = 30,
|
|
.earliestVsync = intended});
|
|
EXPECT_TRUE(result.has_value());
|
|
EXPECT_EQ(900, *result);
|
|
advanceToNextCallback();
|
|
|
|
ASSERT_THAT(cb.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
|
|
ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
|
|
EXPECT_THAT(cb.mWakeupTime[0], 900);
|
|
ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
|
|
EXPECT_THAT(cb.mReadyTime[0], 970);
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) {
|
|
Sequence seq;
|
|
EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq);
|
|
|
|
CountingCallback cb(mDispatch);
|
|
|
|
mDispatch.schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000});
|
|
mDispatch.schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000});
|
|
|
|
advanceToNextCallback();
|
|
|
|
advanceToNextCallback();
|
|
|
|
ASSERT_THAT(cb.mCalls.size(), Eq(1));
|
|
EXPECT_THAT(cb.mCalls[0], Eq(2000));
|
|
ASSERT_THAT(cb.mWakeupTime.size(), Eq(1));
|
|
EXPECT_THAT(cb.mWakeupTime[0], Eq(600));
|
|
ASSERT_THAT(cb.mReadyTime.size(), Eq(1));
|
|
EXPECT_THAT(cb.mReadyTime[0], Eq(2000));
|
|
}
|
|
|
|
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
|
|
protected:
|
|
nsecs_t const mPeriod = 1000;
|
|
nsecs_t const mVsyncMoveThreshold = 200;
|
|
NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
|
|
};
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
|
|
std::string name("basicname");
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
name, [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
EXPECT_THAT(entry.name(), Eq(name));
|
|
EXPECT_FALSE(entry.lastExecutedVsyncTarget());
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
auto const wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(*wakeup, Eq(900));
|
|
|
|
entry.disarm();
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) {
|
|
auto const duration = 500;
|
|
auto const now = 8750;
|
|
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
|
|
.Times(1)
|
|
.WillOnce(Return(10000));
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994},
|
|
mStubTracker, now)
|
|
.has_value());
|
|
auto const wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(*wakeup, Eq(9500));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
|
|
auto callCount = 0;
|
|
auto vsyncCalledTime = 0;
|
|
auto wakeupCalledTime = 0;
|
|
auto readyCalledTime = 0;
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test",
|
|
[&](auto vsyncTime, auto wakeupTime, auto readyTime) {
|
|
callCount++;
|
|
vsyncCalledTime = vsyncTime;
|
|
wakeupCalledTime = wakeupTime;
|
|
readyCalledTime = readyTime;
|
|
},
|
|
mVsyncMoveThreshold);
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
auto const wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(*wakeup, Eq(900));
|
|
|
|
auto const ready = entry.readyTime();
|
|
ASSERT_TRUE(ready);
|
|
EXPECT_THAT(*ready, Eq(1000));
|
|
|
|
entry.callback(entry.executing(), *wakeup, *ready);
|
|
|
|
EXPECT_THAT(callCount, Eq(1));
|
|
EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
|
|
EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
auto lastCalledTarget = entry.lastExecutedVsyncTarget();
|
|
ASSERT_TRUE(lastCalledTarget);
|
|
EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
|
|
.Times(2)
|
|
.WillOnce(Return(1000))
|
|
.WillOnce(Return(1020));
|
|
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
entry.update(mStubTracker, 0);
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
auto wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(wakeup, Eq(900));
|
|
|
|
entry.update(mStubTracker, 0);
|
|
wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(*wakeup, Eq(920));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
entry.update(mStubTracker, 0);
|
|
|
|
auto const wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(*wakeup, Eq(wakeup));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
entry.executing(); // 1000 is executing
|
|
// had 1000 not been executing, this could have been scheduled for time 800.
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
|
|
EXPECT_THAT(*entry.readyTime(), Eq(2000));
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
|
|
EXPECT_THAT(*entry.readyTime(), Eq(2000));
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
|
|
EXPECT_THAT(*entry.readyTime(), Eq(2000));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest,
|
|
willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
|
|
Sequence seq;
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
|
|
.InSequence(seq)
|
|
.WillOnce(Return(1000));
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
|
|
.InSequence(seq)
|
|
.WillOnce(Return(1000));
|
|
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
|
|
.InSequence(seq)
|
|
.WillOnce(Return(2000));
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
|
|
entry.executing(); // 1000 is executing
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) {
|
|
static constexpr auto effectualOffset = 200;
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test", [](auto, auto, auto) {}, mVsyncMoveThreshold);
|
|
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
|
|
entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400});
|
|
entry.addPendingWorkloadUpdate(
|
|
{.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400});
|
|
EXPECT_TRUE(entry.hasPendingWorkloadUpdate());
|
|
entry.update(mStubTracker, 0);
|
|
EXPECT_FALSE(entry.hasPendingWorkloadUpdate());
|
|
EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset));
|
|
}
|
|
|
|
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) {
|
|
auto callCount = 0;
|
|
auto vsyncCalledTime = 0;
|
|
auto wakeupCalledTime = 0;
|
|
auto readyCalledTime = 0;
|
|
VSyncDispatchTimerQueueEntry entry(
|
|
"test",
|
|
[&](auto vsyncTime, auto wakeupTime, auto readyTime) {
|
|
callCount++;
|
|
vsyncCalledTime = vsyncTime;
|
|
wakeupCalledTime = wakeupTime;
|
|
readyCalledTime = readyTime;
|
|
},
|
|
mVsyncMoveThreshold);
|
|
|
|
EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500},
|
|
mStubTracker, 0)
|
|
.has_value());
|
|
auto const wakeup = entry.wakeupTime();
|
|
ASSERT_TRUE(wakeup);
|
|
EXPECT_THAT(*wakeup, Eq(900));
|
|
|
|
auto const ready = entry.readyTime();
|
|
ASSERT_TRUE(ready);
|
|
EXPECT_THAT(*ready, Eq(970));
|
|
|
|
entry.callback(entry.executing(), *wakeup, *ready);
|
|
|
|
EXPECT_THAT(callCount, Eq(1));
|
|
EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
|
|
EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
|
|
EXPECT_FALSE(entry.wakeupTime());
|
|
auto lastCalledTarget = entry.lastExecutedVsyncTarget();
|
|
ASSERT_TRUE(lastCalledTarget);
|
|
EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
|
|
}
|
|
|
|
} // namespace android::scheduler
|
|
|
|
// TODO(b/129481165): remove the #pragma below and fix conversion issues
|
|
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
|