From a0cde48611957ee986f313f5b417342a43e58d3b Mon Sep 17 00:00:00 2001 From: "brianderson@chromium.org" Date: Thu, 15 Aug 2013 01:22:04 +0000 Subject: [PATCH] cc: Handle future timebases properly in DelayBasedTimeSource The existing code didn't anticipate negative values for intervals_elapsed, which would round up towards 0 instead of down towards 0 and cause the next frame to be posted more than 1 vsync away. This primarly cropped up on Windows machines, but only for certain configurations. BUG=262437 Review URL: https://chromiumcodereview.appspot.com/22887010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217723 0039d316-1c4b-4281-b951-d872f2087c98 --- cc/scheduler/delay_based_time_source.cc | 14 ++- cc/scheduler/delay_based_time_source_unittest.cc | 109 +++++++++++++++++++++++ 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/cc/scheduler/delay_based_time_source.cc b/cc/scheduler/delay_based_time_source.cc index 1b162c9c8860..412fd9a0064d 100644 --- a/cc/scheduler/delay_based_time_source.cc +++ b/cc/scheduler/delay_based_time_source.cc @@ -193,9 +193,17 @@ base::TimeTicks DelayBasedTimeSource::Now() const { base::TimeTicks DelayBasedTimeSource::NextTickTarget(base::TimeTicks now) { const base::TimeDelta epsilon(base::TimeDelta::FromMicroseconds(1)); base::TimeDelta new_interval = next_parameters_.interval; - int intervals_elapsed = - (now - next_parameters_.tick_target + new_interval - epsilon) / - new_interval; + + // Integer division rounds towards 0, but we always want to round down the + // number of intervals_elapsed, so we need the extra condition here. + int intervals_elapsed; + if (next_parameters_.tick_target < now) { + intervals_elapsed = + (now - next_parameters_.tick_target + new_interval - epsilon) / + new_interval; + } else { + intervals_elapsed = (now - next_parameters_.tick_target) / new_interval; + } base::TimeTicks new_tick_target = next_parameters_.tick_target + new_interval * intervals_elapsed; DCHECK(now <= new_tick_target) diff --git a/cc/scheduler/delay_based_time_source_unittest.cc b/cc/scheduler/delay_based_time_source_unittest.cc index ac54f57a6344..b018dc369c67 100644 --- a/cc/scheduler/delay_based_time_source_unittest.cc +++ b/cc/scheduler/delay_based_time_source_unittest.cc @@ -305,6 +305,115 @@ TEST(DelayBasedTimeSource, HanldlesSignificantIntervalChangesImmediately) { EXPECT_EQ(16, task_runner->NextPendingTaskDelay().InMilliseconds()); } +TEST(DelayBasedTimeSource, JitteryRuntimeWithFutureTimebases) { + scoped_refptr task_runner = + new base::TestSimpleTaskRunner; + FakeTimeSourceClient client; + scoped_refptr timer = + FakeDelayBasedTimeSource::Create(Interval(), task_runner.get()); + timer->SetClient(&client); + 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(17, 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(24, 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(31, 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; -- 2.11.4.GIT