diff --git a/engine/src/flutter/scheduler/BUILD.gn b/engine/src/flutter/scheduler/BUILD.gn new file mode 100644 index 0000000000..acb155a08e --- /dev/null +++ b/engine/src/flutter/scheduler/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright 2014 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. + +static_library("scheduler") { + sources = [ + "scheduler.cc", + "scheduler.h", + "time_interval.cc", + "time_interval.h", + "timer.cc", + "timer.h", + ] + + deps = [ + "//base", + ] +} diff --git a/engine/src/flutter/scheduler/scheduler.cc b/engine/src/flutter/scheduler/scheduler.cc new file mode 100644 index 0000000000..371d49db0e --- /dev/null +++ b/engine/src/flutter/scheduler/scheduler.cc @@ -0,0 +1,45 @@ +// Copyright 2014 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 "sky/scheduler/scheduler.h" + +namespace sky { + +Scheduler::Client::~Client() { +} + +Scheduler::Scheduler(Client* client, + scoped_refptr task_runner) + : client_(client), timer_(this, task_runner) { +} + +Scheduler::~Scheduler() { +} + +void Scheduler::SetNeedsFrame() { + timer_.SetEnabled(true); +} + +void Scheduler::UpdateFrameDuration(base::TimeDelta estimate) { + frame_duration_ = estimate; + UpdateTimerInterval(); +} + +void Scheduler::UpdateVSync(const TimeInterval& vsync) { + vsync_ = vsync; + UpdateTimerInterval(); +} + +void Scheduler::UpdateTimerInterval() { + TimeInterval interval = vsync_; + interval.base -= frame_duration_; + timer_.SetInterval(interval); +} + +void Scheduler::OnTimerTick(base::TimeTicks now) { + timer_.SetEnabled(false); + client_->BeginFrame(now, vsync_.NextAfter(now)); + // We might be deleted here. +} +} diff --git a/engine/src/flutter/scheduler/scheduler.h b/engine/src/flutter/scheduler/scheduler.h new file mode 100644 index 0000000000..05aace9e18 --- /dev/null +++ b/engine/src/flutter/scheduler/scheduler.h @@ -0,0 +1,45 @@ +// Copyright 2014 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. + +#ifndef SKY_SCHEDULER_SCHEDULER_H_ +#define SKY_SCHEDULER_SCHEDULER_H_ + +#include "base/single_thread_task_runner.h" +#include "base/time/time.h" +#include "sky/scheduler/timer.h" + +namespace sky { + +class Scheduler : public Timer::Client { + public: + class Client { + public: + virtual void BeginFrame(base::TimeTicks frame_time, + base::TimeTicks deadline) = 0; + + protected: + virtual ~Client(); + }; + + Scheduler(Client* client, + scoped_refptr task_runner); + ~Scheduler(); + + void UpdateFrameDuration(base::TimeDelta estimate); + void UpdateVSync(const TimeInterval& vsync); + + void SetNeedsFrame(); + + private: + void UpdateTimerInterval(); + void OnTimerTick(base::TimeTicks now) override; + + Client* client_; + Timer timer_; + TimeInterval vsync_; + base::TimeDelta frame_duration_; +}; +} + +#endif // SKY_SCHEDULER_SCHEDULER_H_ diff --git a/engine/src/flutter/scheduler/time_interval.cc b/engine/src/flutter/scheduler/time_interval.cc new file mode 100644 index 0000000000..424744851f --- /dev/null +++ b/engine/src/flutter/scheduler/time_interval.cc @@ -0,0 +1,17 @@ +// Copyright 2014 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 "sky/scheduler/time_interval.h" + +#include + +namespace sky { + +base::TimeTicks TimeInterval::NextAfter(base::TimeTicks when) { + int64 offset = std::abs((when - base).ToInternalValue()); + base::TimeDelta excess = + base::TimeDelta::FromInternalValue(offset % duration.ToInternalValue()); + return when + duration - excess; +} +} diff --git a/engine/src/flutter/scheduler/time_interval.h b/engine/src/flutter/scheduler/time_interval.h new file mode 100644 index 0000000000..2b008902fd --- /dev/null +++ b/engine/src/flutter/scheduler/time_interval.h @@ -0,0 +1,24 @@ +// Copyright 2014 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. + +#ifndef SKY_SCHEDULER_TIME_INTERVAL_H_ +#define SKY_SCHEDULER_TIME_INTERVAL_H_ + +#include "base/time/time.h" + +namespace sky { + +struct TimeInterval { + TimeInterval() {} + TimeInterval(base::TimeTicks base, base::TimeDelta duration) + : base(base), duration(duration) {} + + base::TimeTicks NextAfter(base::TimeTicks when); + + base::TimeTicks base; + base::TimeDelta duration; +}; +} + +#endif // SKY_SCHEDULER_TIME_INTERVAL_H_ diff --git a/engine/src/flutter/scheduler/timer.cc b/engine/src/flutter/scheduler/timer.cc new file mode 100644 index 0000000000..f708da0ea9 --- /dev/null +++ b/engine/src/flutter/scheduler/timer.cc @@ -0,0 +1,94 @@ +// Copyright 2014 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 "sky/scheduler/timer.h" + +#include + +#include "base/bind.h" +#include "base/tracked_objects.h" + +namespace sky { + +// We're willing to slop around 1/4 of a tick duration to avoid trashing our +// client with irregular ticks. +static const int64 kTickSlop = 4; + +Timer::Client::~Client() { +} + +Timer::Timer(Client* client, + scoped_refptr task_runner) + : client_(client), + task_runner_(task_runner), + enabled_(false), + weak_factory_(this) { + DCHECK(client_); +} + +Timer::~Timer() { +} + +void Timer::SetEnabled(bool enabled) { + enabled_ = enabled; + + if (enabled_ && current_target_.is_null()) + ScheduleNextTick(base::TimeTicks::Now()); +} + +void Timer::SetInterval(const TimeInterval& interval) { + interval_ = interval; + + // We don't have a tick scheduled, so there's no need to reschedule it. + if (current_target_.is_null()) + return; + + base::TimeTicks now = base::TimeTicks::Now(); + + base::TimeTicks new_target = NextTickTarget(now); + base::TimeDelta delta = base::TimeDelta::FromInternalValue( + std::abs((new_target - current_target_).ToInternalValue())); + + if (delta * kTickSlop < interval_.duration) + return; + + current_target_ = base::TimeTicks(); + weak_factory_.InvalidateWeakPtrs(); + PostTickTask(now, new_target); +} + +base::TimeTicks Timer::NextTickTarget(base::TimeTicks now) { + base::TimeTicks target = interval_.NextAfter(now); + + // If we're targeting a time that's too soon since the last tick, we push out + // the target to the next tick. + if ((target - last_tick_) * kTickSlop < interval_.duration) + target += interval_.duration; + + return target; +} + +void Timer::ScheduleNextTick(base::TimeTicks now) { + PostTickTask(now, NextTickTarget(now)); +} + +void Timer::PostTickTask(base::TimeTicks now, base::TimeTicks target) { + DCHECK(current_target_.is_null()); + current_target_ = target; + task_runner_->PostDelayedTask( + FROM_HERE, base::Bind(&Timer::OnTimerFired, weak_factory_.GetWeakPtr()), + current_target_ - now); +} + +void Timer::OnTimerFired() { + current_target_ = base::TimeTicks(); + if (!enabled_) + return; + base::TimeTicks now = base::TimeTicks::Now(); + ScheduleNextTick(now); + last_tick_ = now; + client_->OnTimerTick(now); + // We might be deleted here. +} +} diff --git a/engine/src/flutter/scheduler/timer.h b/engine/src/flutter/scheduler/timer.h new file mode 100644 index 0000000000..df20642c8b --- /dev/null +++ b/engine/src/flutter/scheduler/timer.h @@ -0,0 +1,52 @@ +// Copyright 2014 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. + +#ifndef SKY_SCHEDULER_TIMER_H_ +#define SKY_SCHEDULER_TIMER_H_ + +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/time/time.h" +#include "sky/scheduler/time_interval.h" + +namespace sky { + +class Timer { + public: + class Client { + public: + virtual void OnTimerTick(base::TimeTicks now) = 0; + + protected: + virtual ~Client(); + }; + + Timer(Client* client, + scoped_refptr task_runner); + ~Timer(); + + void SetInterval(const TimeInterval& parameters); + void SetEnabled(bool enabled); + + private: + base::TimeTicks NextTickTarget(base::TimeTicks now); + void ScheduleNextTick(base::TimeTicks now); + void PostTickTask(base::TimeTicks now, base::TimeTicks target); + void OnTimerFired(); + + Client* client_; + scoped_refptr task_runner_; + + TimeInterval interval_; + base::TimeTicks last_tick_; + base::TimeTicks current_target_; + + bool enabled_; + + base::WeakPtrFactory weak_factory_; +}; + +} + +#endif // SKY_SCHEDULER_TIMER_H_