| // Copyright 2011 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 "cc/scheduler/delay_based_time_source.h" |
| |
| #include <stdint.h> |
| |
| #include "base/test/test_simple_task_runner.h" |
| #include "cc/test/scheduler_test_common.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace cc { |
| namespace { |
| |
| base::TimeDelta Interval() { |
| return base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond / |
| 60); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, TaskPostedAndTickCalled) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| EXPECT_TRUE(timer->Active()); |
| EXPECT_TRUE(task_runner->HasPendingTask()); |
| |
| timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(16)); |
| task_runner->RunPendingTasks(); |
| EXPECT_TRUE(timer->Active()); |
| EXPECT_TRUE(client.TickCalled()); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, TickNotCalledWithTaskPosted) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| EXPECT_TRUE(task_runner->HasPendingTask()); |
| timer->SetActive(false); |
| task_runner->RunPendingTasks(); |
| EXPECT_FALSE(client.TickCalled()); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, StartTwiceEnqueuesOneTask) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| EXPECT_TRUE(task_runner->HasPendingTask()); |
| task_runner->ClearPendingTasks(); |
| timer->SetActive(true); |
| EXPECT_FALSE(task_runner->HasPendingTask()); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, StartWhenRunningDoesntTick) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| EXPECT_TRUE(task_runner->HasPendingTask()); |
| task_runner->RunPendingTasks(); |
| task_runner->ClearPendingTasks(); |
| timer->SetActive(true); |
| EXPECT_FALSE(task_runner->HasPendingTask()); |
| } |
| |
| // At 60Hz, when the tick returns at exactly the requested next time, make sure |
| // a 16ms next delay is posted. |
| TEST(DelayBasedTimeSourceTest, NextDelaySaneWhenExactlyOnRequestedTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| // Run the first tick. |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| timer->SetNow(timer->Now() + Interval()); |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| // At 60Hz, when the tick returns at slightly after the requested next time, |
| // make sure a 16ms next delay is posted. |
| TEST(DelayBasedTimeSourceTest, NextDelaySaneWhenSlightlyAfterRequestedTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| // Run the first tick. |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| timer->SetNow(timer->Now() + Interval() + |
| base::TimeDelta::FromMicroseconds(1)); |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| // At 60Hz, when the tick returns at exactly 2*interval after the requested next |
| // time, make sure we don't tick unnecessarily. |
| TEST(DelayBasedTimeSourceTest, |
| NextDelaySaneWhenExactlyTwiceAfterRequestedTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| // Run the first tick. |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| timer->SetNow(timer->Now() + 2 * Interval()); |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| // At 60Hz, when the tick returns at 2*interval and a bit after the requested |
| // next time, make sure a 16ms next delay is posted. |
| TEST(DelayBasedTimeSourceTest, |
| NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| // Run the first tick. |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| timer->SetNow(timer->Now() + 2 * Interval() + |
| base::TimeDelta::FromMicroseconds(1)); |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| // At 60Hz, when the tick returns halfway to the next frame time, make sure |
| // a correct next delay value is posted. |
| TEST(DelayBasedTimeSourceTest, NextDelaySaneWhenHalfAfterRequestedTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| // Run the first tick. |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| timer->SetNow(timer->Now() + Interval() + |
| base::TimeDelta::FromMilliseconds(8)); |
| task_runner->RunPendingTasks(); |
| |
| EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, JitteryRuntimeWithFutureTimebases) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| |
| // Run the first tick. |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| base::TimeTicks future_timebase = timer->Now() + Interval() * 10; |
| |
| // 1ms jitter |
| base::TimeDelta jitter1 = base::TimeDelta::FromMilliseconds(1); |
| |
| // Tick with +1ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() + jitter1); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with 0ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() - jitter1); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with -1ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() - jitter1); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(1, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with 0ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() + jitter1); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // 8 ms jitter |
| base::TimeDelta jitter8 = base::TimeDelta::FromMilliseconds(8); |
| |
| // Tick with +8ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() + jitter8); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with 0ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() - jitter8); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with -8ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() - jitter8); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(8, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with 0ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() + jitter8); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // 15 ms jitter |
| base::TimeDelta jitter15 = base::TimeDelta::FromMilliseconds(15); |
| |
| // Tick with +15ms jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() + jitter15); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(1, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with 0ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() - jitter15); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with -15ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() - jitter15); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(15, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| |
| // Tick with 0ms of jitter |
| future_timebase += Interval(); |
| timer->SetTimebaseAndInterval(future_timebase, Interval()); |
| timer->SetNow(timer->Now() + Interval() + jitter15); |
| task_runner->RunPendingTasks(); |
| EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) { |
| int num_iterations = 10; |
| |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); |
| |
| double total_frame_time = 0.0; |
| for (int i = 0; i < num_iterations; ++i) { |
| int64_t delay_ms = task_runner->NextPendingTaskDelay().InMilliseconds(); |
| |
| // accumulate the "delay" |
| total_frame_time += delay_ms / 1000.0; |
| |
| // Run the callback exactly when asked |
| timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(delay_ms)); |
| task_runner->RunPendingTasks(); |
| } |
| double average_interval = |
| total_frame_time / static_cast<double>(num_iterations); |
| EXPECT_NEAR(1.0 / 60.0, average_interval, 0.1); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, TestDeactivateWhilePending) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| timer->SetActive(true); // Should post a task. |
| timer->SetActive(false); |
| timer = NULL; |
| // Should run the posted task without crashing. |
| EXPECT_TRUE(task_runner->HasPendingTask()); |
| task_runner->RunPendingTasks(); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, TestDeactivateAndReactivateBeforeNextTickTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| |
| // Should run the activate task, and pick up a new timebase. |
| timer->SetActive(true); |
| task_runner->RunPendingTasks(); |
| |
| // Stop the timer |
| timer->SetActive(false); |
| |
| // Task will be pending anyway, run it |
| task_runner->RunPendingTasks(); |
| |
| // Start the timer again, but before the next tick time the timer previously |
| // planned on using. That same tick time should still be targeted. |
| timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(4)); |
| timer->SetActive(true); |
| EXPECT_EQ(12, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| TEST(DelayBasedTimeSourceTest, TestDeactivateAndReactivateAfterNextTickTime) { |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner = |
| new base::TestSimpleTaskRunner; |
| FakeDelayBasedTimeSourceClient client; |
| std::unique_ptr<FakeDelayBasedTimeSource> timer( |
| new FakeDelayBasedTimeSource(task_runner.get())); |
| timer->SetClient(&client); |
| timer->SetTimebaseAndInterval(base::TimeTicks(), Interval()); |
| |
| // Should run the activate task, and pick up a new timebase. |
| timer->SetActive(true); |
| task_runner->RunPendingTasks(); |
| |
| // Stop the timer. |
| timer->SetActive(false); |
| |
| // Task will be pending anyway, run it. |
| task_runner->RunPendingTasks(); |
| |
| // Start the timer again, but before the next tick time the timer previously |
| // planned on using. That same tick time should still be targeted. |
| timer->SetNow(timer->Now() + base::TimeDelta::FromMilliseconds(20)); |
| timer->SetActive(true); |
| EXPECT_EQ(13, task_runner->NextPendingTaskDelay().InMilliseconds()); |
| } |
| |
| } // namespace |
| } // namespace cc |