Index: services/gfx/compositor/backend/vsync_scheduler_unittest.cc |
diff --git a/services/gfx/compositor/backend/vsync_scheduler_unittest.cc b/services/gfx/compositor/backend/vsync_scheduler_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..40989ea0bb93e144449eebf6b08a1bd9aebdf779 |
--- /dev/null |
+++ b/services/gfx/compositor/backend/vsync_scheduler_unittest.cc |
@@ -0,0 +1,365 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "services/gfx/compositor/backend/vsync_scheduler.h" |
+ |
+#include <queue> |
+ |
+#include "base/bind.h" |
+#include "base/test/test_mock_time_task_runner.h" |
+#include "base/time/tick_clock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace compositor { |
+ |
+static constexpr int64_t kVsyncTimebase = -5000; |
+static constexpr int64_t kVsyncInterval = 10000; |
+static constexpr int64_t kUpdatePhase = -9000; |
+static constexpr int64_t kSnapshotPhase = -1000; |
+static constexpr int64_t kPresentationPhase = 2000; |
+ |
+class VsyncSchedulerTest : public testing::Test { |
+ protected: |
+ void SetUp() override { Reset(); } |
+ |
+ void TearDown() override { |
+ task_runner_->FastForwardUntilNoTasksRemain(); |
+ EXPECT_TRUE(expected_callbacks_.empty()); |
+ } |
+ |
+ void ExpectUpdateCallback(int64_t frame_time, |
+ uint64_t frame_interval, |
+ int64_t frame_deadline, |
+ int64_t presentation_time) { |
+ expected_callbacks_.emplace(CallbackType::kUpdate, frame_time, frame_time, |
+ frame_interval, frame_deadline, |
+ presentation_time); |
+ } |
+ |
+ void ExpectSnapshotCallback(int64_t frame_time, |
+ uint64_t frame_interval, |
+ int64_t frame_deadline, |
+ int64_t presentation_time) { |
+ expected_callbacks_.emplace(CallbackType::kSnapshot, frame_deadline, |
+ frame_time, frame_interval, frame_deadline, |
+ presentation_time); |
+ } |
+ |
+ MojoTimeTicks GetTimeTicksNow() { |
+ return task_runner_->NowTicks().ToInternalValue(); |
+ } |
+ |
+ void Reset() { |
+ task_runner_ = new base::TestMockTimeTaskRunner(); |
+ SchedulerCallbacks callbacks( |
+ base::Bind(&VsyncSchedulerTest::OnUpdate, base::Unretained(this)), |
+ base::Bind(&VsyncSchedulerTest::OnSnapshot, base::Unretained(this))); |
+ scheduler_.reset( |
+ new VsyncScheduler(task_runner_, callbacks, |
+ base::Bind(&VsyncSchedulerTest::GetTimeTicksNow, |
+ base::Unretained(this)))); |
+ |
+ std::queue<ExpectedCallback> victim; |
+ expected_callbacks_.swap(victim); |
+ } |
+ |
+ void FastForwardTo(int64_t time) { |
+ DCHECK(time >= GetTimeTicksNow()); |
+ task_runner_->FastForwardBy( |
+ base::TimeDelta::FromMicroseconds(time - GetTimeTicksNow())); |
+ } |
+ |
+ std::unique_ptr<VsyncScheduler> scheduler_; |
+ |
+ private: |
+ enum class CallbackType { |
+ kUpdate, |
+ kSnapshot, |
+ }; |
+ |
+ struct ExpectedCallback { |
+ ExpectedCallback(CallbackType type, |
+ int64_t delivery_time, |
+ int64_t frame_time, |
+ uint64_t frame_interval, |
+ int64_t frame_deadline, |
+ int64_t presentation_time) |
+ : type(type), |
+ delivery_time(delivery_time), |
+ frame_time(frame_time), |
+ frame_interval(frame_interval), |
+ frame_deadline(frame_deadline), |
+ presentation_time(presentation_time) {} |
+ |
+ CallbackType type; |
+ int64_t delivery_time; |
+ int64_t frame_time; |
+ uint64_t frame_interval; |
+ int64_t frame_deadline; |
+ int64_t presentation_time; |
+ }; |
+ |
+ void OnUpdate(const mojo::gfx::composition::FrameInfo& frame_info) { |
+ VerifyCallback(CallbackType::kUpdate, frame_info); |
+ } |
+ |
+ void OnSnapshot(const mojo::gfx::composition::FrameInfo& frame_info) { |
+ VerifyCallback(CallbackType::kSnapshot, frame_info); |
+ } |
+ |
+ void VerifyCallback(CallbackType type, |
+ const mojo::gfx::composition::FrameInfo& frame_info) { |
+ EXPECT_FALSE(expected_callbacks_.empty()); |
+ if (!expected_callbacks_.empty()) { |
+ const ExpectedCallback& c = expected_callbacks_.front(); |
+ EXPECT_EQ(static_cast<int>(c.type), static_cast<int>(type)); |
+ EXPECT_EQ(c.delivery_time, GetTimeTicksNow()); |
+ EXPECT_EQ(c.frame_time, frame_info.frame_time); |
+ EXPECT_EQ(c.frame_interval, frame_info.frame_interval); |
+ EXPECT_EQ(c.frame_deadline, frame_info.frame_deadline); |
+ EXPECT_EQ(c.presentation_time, frame_info.presentation_time); |
+ expected_callbacks_.pop(); |
+ } |
+ } |
+ |
+ scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; |
+ std::queue<ExpectedCallback> expected_callbacks_; |
+}; |
+ |
+TEST_F(VsyncSchedulerTest, StartValidatesArguments) { |
+ // Vsync timebase is in the past. |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ Reset(); |
+ |
+ // Vsync timebase is now. (current time == 0) |
+ EXPECT_TRUE(scheduler_->Start(0, kVsyncInterval, kUpdatePhase, kSnapshotPhase, |
+ kPresentationPhase)); |
+ Reset(); |
+ |
+ // Vsync timebase in the future. (current time == 0) |
+ EXPECT_FALSE(scheduler_->Start(1, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Vsync interval too small. |
+ EXPECT_FALSE( |
+ scheduler_->Start(0, VsyncScheduler::kMinVsyncInterval - 1, 0, 0, 0)); |
+ |
+ // Vsync interval at minimum. |
+ EXPECT_TRUE(scheduler_->Start(0, VsyncScheduler::kMinVsyncInterval, 0, 0, 0)); |
+ Reset(); |
+ |
+ // Vsync interval at maximum. |
+ EXPECT_TRUE(scheduler_->Start(0, VsyncScheduler::kMaxVsyncInterval, 0, 0, 0)); |
+ Reset(); |
+ |
+ // Vsync interval too large. |
+ EXPECT_FALSE( |
+ scheduler_->Start(0, VsyncScheduler::kMaxVsyncInterval + 1, 0, 0, 0)); |
+ |
+ // Snapshot phase earlier than update phase. |
+ EXPECT_FALSE(scheduler_->Start(0, kVsyncInterval, kUpdatePhase, |
+ kUpdatePhase - 1, kPresentationPhase)); |
+ |
+ // Snapshot phase more than one frame behind update phase. |
+ EXPECT_FALSE(scheduler_->Start(0, kVsyncInterval, kUpdatePhase, |
+ kUpdatePhase + kVsyncInterval + 1, |
+ kPresentationPhase)); |
+ |
+ // Presentation phase earlier than snapshot phase. |
+ EXPECT_FALSE(scheduler_->Start(0, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kSnapshotPhase - 1)); |
+ |
+ // Minimum and maximum update vs. snapshot phase delta. |
+ EXPECT_TRUE(scheduler_->Start(0, kVsyncInterval, kUpdatePhase, kUpdatePhase, |
+ kUpdatePhase)); |
+ Reset(); |
+ EXPECT_TRUE(scheduler_->Start(0, kVsyncInterval, kUpdatePhase, |
+ kUpdatePhase + kVsyncInterval, |
+ kUpdatePhase + kVsyncInterval)); |
+ Reset(); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, ScheduleRedundantSnapshot) { |
+ // Start immediately schedules work. |
+ ExpectSnapshotCallback(-4000, kVsyncInterval, 4000, 7000); |
+ ExpectUpdateCallback(6000, kVsyncInterval, 14000, 17000); |
+ ExpectSnapshotCallback(6000, kVsyncInterval, 14000, 17000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Shortly after the first update, schedule another snapshot. |
+ // Nothing happens because a snapshot is still due at 14000. |
+ FastForwardTo(8000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kSnapshot); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, ScheduleRedundantUpdate) { |
+ // Start immediately schedules work. |
+ ExpectSnapshotCallback(-4000, kVsyncInterval, 4000, 7000); |
+ ExpectUpdateCallback(6000, kVsyncInterval, 14000, 17000); |
+ ExpectSnapshotCallback(6000, kVsyncInterval, 14000, 17000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Before the first update, schedule another update. |
+ // Nothing happens because an update is still due at 6000. |
+ FastForwardTo(5000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, ScheduleRequiredSnapshot) { |
+ // Start immediately schedules work. |
+ ExpectSnapshotCallback(-4000, kVsyncInterval, 4000, 7000); |
+ ExpectUpdateCallback(6000, kVsyncInterval, 14000, 17000); |
+ ExpectSnapshotCallback(6000, kVsyncInterval, 14000, 17000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Shortly after the last snapshot, schedule another snapshot. |
+ FastForwardTo(15000); |
+ ExpectUpdateCallback(16000, kVsyncInterval, 24000, 27000); |
+ ExpectSnapshotCallback(16000, kVsyncInterval, 24000, 27000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kSnapshot); |
+ |
+ // Exactly at the moment of the next snapshot, schedule another snapshot. |
+ FastForwardTo(24000); |
+ ExpectUpdateCallback(26000, kVsyncInterval, 34000, 37000); |
+ ExpectSnapshotCallback(26000, kVsyncInterval, 34000, 37000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kSnapshot); |
+ |
+ // A long time thereafter, with no time to update, schedule another snapshot. |
+ FastForwardTo(53000); |
+ ExpectSnapshotCallback(46000, kVsyncInterval, 54000, 57000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kSnapshot); |
+ |
+ // A long time thereafter, with time to update, schedule another snapshot. |
+ FastForwardTo(75000); |
+ ExpectUpdateCallback(76000, kVsyncInterval, 84000, 87000); |
+ ExpectSnapshotCallback(76000, kVsyncInterval, 84000, 87000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kSnapshot); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, ScheduleRequiredUpdate) { |
+ // Start immediately schedules work. |
+ ExpectSnapshotCallback(-4000, kVsyncInterval, 4000, 7000); |
+ ExpectUpdateCallback(6000, kVsyncInterval, 14000, 17000); |
+ ExpectSnapshotCallback(6000, kVsyncInterval, 14000, 17000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Shortly after the first update, schedule another update. |
+ FastForwardTo(8000); |
+ ExpectUpdateCallback(16000, kVsyncInterval, 24000, 27000); |
+ ExpectSnapshotCallback(16000, kVsyncInterval, 24000, 27000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ |
+ // Exactly at the moment of the next update, schedule another update. |
+ FastForwardTo(16000); |
+ ExpectUpdateCallback(26000, kVsyncInterval, 34000, 37000); |
+ ExpectSnapshotCallback(26000, kVsyncInterval, 34000, 37000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ |
+ // A long time thereafter, with no time to snapshot, schedule another update. |
+ FastForwardTo(55000); |
+ ExpectUpdateCallback(56000, kVsyncInterval, 64000, 67000); |
+ ExpectSnapshotCallback(56000, kVsyncInterval, 64000, 67000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ |
+ // A long time thereafter, with time to snapshot, schedule another update. |
+ FastForwardTo(83000); |
+ ExpectSnapshotCallback(76000, kVsyncInterval, 84000, 87000); |
+ ExpectUpdateCallback(86000, kVsyncInterval, 94000, 97000); |
+ ExpectSnapshotCallback(86000, kVsyncInterval, 94000, 97000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, StartAndStop) { |
+ // Scheduling frames before start does nothing. |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ |
+ // Starting the scheduler automatically schedules an update. |
+ FastForwardTo(15000); |
+ ExpectUpdateCallback(16000, kVsyncInterval, 24000, 27000); |
+ ExpectSnapshotCallback(16000, kVsyncInterval, 24000, 27000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Stopping the scheduler suspends further updates. |
+ FastForwardTo(24000); |
+ scheduler_->Stop(); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ |
+ // Restarting scheduling resumes updates. |
+ FastForwardTo(53000); |
+ ExpectSnapshotCallback(46000, kVsyncInterval, 54000, 57000); |
+ ExpectUpdateCallback(56000, kVsyncInterval, 64000, 67000); |
+ ExpectSnapshotCallback(56000, kVsyncInterval, 64000, 67000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Stopping the scheduler cancels undelivered updates. |
+ FastForwardTo(63000); |
+ // canceled: ExpectUpdateCallback(66000, kVsyncInterval, 74000, 77000); |
+ // canceled: ExpectSnapshotCallback(66000, kVsyncInterval, 74000, 77000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ FastForwardTo(65000); |
+ scheduler_->Stop(); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, RedundantStart) { |
+ // Start immediately schedules work. |
+ ExpectSnapshotCallback(-4000, kVsyncInterval, 4000, 7000); |
+ ExpectUpdateCallback(6000, kVsyncInterval, 14000, 17000); |
+ ExpectSnapshotCallback(6000, kVsyncInterval, 14000, 17000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // Doing it again has no added effect. |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // A long time thereafter, schedule another update. |
+ FastForwardTo(55000); |
+ ExpectUpdateCallback(56000, kVsyncInterval, 64000, 67000); |
+ ExpectSnapshotCallback(56000, kVsyncInterval, 64000, 67000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+} |
+ |
+TEST_F(VsyncSchedulerTest, StartWithNewParameters) { |
+ // Start immediately schedules work. |
+ ExpectSnapshotCallback(-4000, kVsyncInterval, 4000, 7000); |
+ ExpectUpdateCallback(6000, kVsyncInterval, 14000, 17000); |
+ ExpectSnapshotCallback(6000, kVsyncInterval, 14000, 17000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+ |
+ // After the snapshot is delivered, change parameters. |
+ FastForwardTo(14000); |
+ ExpectUpdateCallback(17000, kVsyncInterval * 2, 33000, 39000); |
+ ExpectSnapshotCallback(17000, kVsyncInterval * 2, 33000, 39000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval * 2, |
+ kUpdatePhase * 2, kSnapshotPhase * 2, |
+ kPresentationPhase * 2)); |
+ |
+ // Schedule another update with these parameters. |
+ FastForwardTo(18000); |
+ ExpectUpdateCallback(37000, kVsyncInterval * 2, 53000, 59000); |
+ // canceled: ExpectSnapshotCallback(37000, kVsyncInterval * 2, 53000, 59000); |
+ scheduler_->ScheduleFrame(SchedulingMode::kUpdateAndSnapshot); |
+ |
+ // At the moment when the update is delivered, change parameters again. |
+ // We're too late to cancel the prior update but we do cancel the prior |
+ // snapshot and we'll follow it up with another update with the new |
+ // parameters. |
+ FastForwardTo(37000); |
+ ExpectUpdateCallback(46000, kVsyncInterval, 54000, 57000); |
+ ExpectSnapshotCallback(46000, kVsyncInterval, 54000, 57000); |
+ EXPECT_TRUE(scheduler_->Start(kVsyncTimebase, kVsyncInterval, kUpdatePhase, |
+ kSnapshotPhase, kPresentationPhase)); |
+} |
+ |
+// TODO(jeffbrown): Add tests for cases where the compositor has fallen behind. |
+ |
+} // namespace compositor |