Commit 9d86ac5e authored by Florian Loitsch's avatar Florian Loitsch

Merge pull request #664 from floitschG/scheduler.renames

Rename some of the functions from the scheduler.
parents c13fe8eb 72821152
...@@ -40,7 +40,7 @@ class Ticker { ...@@ -40,7 +40,7 @@ class Ticker {
_startTime = null; _startTime = null;
if (_animationId != null) { if (_animationId != null) {
scheduler.cancelAnimationFrame(_animationId); scheduler.cancelFrameCallbackWithId(_animationId);
_animationId = null; _animationId = null;
} }
...@@ -74,6 +74,6 @@ class Ticker { ...@@ -74,6 +74,6 @@ class Ticker {
void _scheduleTick() { void _scheduleTick() {
assert(isTicking); assert(isTicking);
assert(_animationId == null); assert(_animationId == null);
_animationId = scheduler.requestAnimationFrame(_tick); _animationId = scheduler.scheduleFrameCallback(_tick);
} }
} }
...@@ -17,7 +17,7 @@ double timeDilation = 1.0; ...@@ -17,7 +17,7 @@ double timeDilation = 1.0;
/// scheduler's epoch. Use timeStamp to determine how far to advance animation /// scheduler's epoch. Use timeStamp to determine how far to advance animation
/// timelines so that all the animations in the system are synchronized to a /// timelines so that all the animations in the system are synchronized to a
/// common time base. /// common time base.
typedef void SchedulerCallback(Duration timeStamp); typedef void FrameCallback(Duration timeStamp);
typedef void SchedulerExceptionHandler(dynamic exception, StackTrace stack); typedef void SchedulerExceptionHandler(dynamic exception, StackTrace stack);
/// This callback is invoked whenever an exception is caught by the scheduler. /// This callback is invoked whenever an exception is caught by the scheduler.
...@@ -29,11 +29,11 @@ SchedulerExceptionHandler debugSchedulerExceptionHandler; ...@@ -29,11 +29,11 @@ SchedulerExceptionHandler debugSchedulerExceptionHandler;
/// An entry in the scheduler's priority queue. /// An entry in the scheduler's priority queue.
/// ///
/// Combines the task and its priority. /// Combines the task and its priority.
class _SchedulerEntry { class _TaskEntry {
final ui.VoidCallback task; final ui.VoidCallback task;
final int priority; final int priority;
const _SchedulerEntry(this.task, this.priority); const _TaskEntry(this.task, this.priority);
} }
class Priority { class Priority {
...@@ -83,102 +83,138 @@ class Priority { ...@@ -83,102 +83,138 @@ class Priority {
class Scheduler { class Scheduler {
/// Requires clients to use the [scheduler] singleton /// Requires clients to use the [scheduler] singleton
Scheduler._() { Scheduler._() {
ui.window.onBeginFrame = beginFrame; ui.window.onBeginFrame = handleBeginFrame;
} }
SchedulingStrategy schedulingStrategy = new DefaultSchedulingStrategy(); SchedulingStrategy schedulingStrategy = new DefaultSchedulingStrategy();
final PriorityQueue _queue = new HeapPriorityQueue<_SchedulerEntry>( final PriorityQueue _taskQueue = new HeapPriorityQueue<_TaskEntry>(
(_SchedulerEntry e1, _SchedulerEntry e2) { (_TaskEntry e1, _TaskEntry e2) {
// Note that we inverse the priority. // Note that we inverse the priority.
return -e1.priority.compareTo(e2.priority); return -e1.priority.compareTo(e2.priority);
} }
); );
/// Wether this scheduler already requested to be woken up as soon as /// Whether this scheduler already requested to be called from the event loop.
/// possible. bool _hasRequestedAnEventLoopCallback = false;
bool _wakingNow = false;
/// Wether this scheduler already requested to be woken up in the next frame. /// Whether this scheduler already requested to be called at the beginning of
bool _wakingNextFrame = false; /// the next frame.
bool _hasRequestedABeginFrameCallback = false;
/// Schedules the given [task] with the given [priority]. /// Schedules the given [task] with the given [priority].
void scheduleTask(ui.VoidCallback task, Priority priority) { void scheduleTask(ui.VoidCallback task, Priority priority) {
bool isFirstTask = _queue.isEmpty; bool isFirstTask = _taskQueue.isEmpty;
_queue.add(new _SchedulerEntry(task, priority._value)); _taskQueue.add(new _TaskEntry(task, priority._value));
if (isFirstTask) if (isFirstTask)
_wakeNow(); _ensureEventLoopCallback();
} }
/// Invoked by the system when there is time to run tasks. /// Invoked by the system when there is time to run tasks.
void tick() { void handleEventLoopCallback() {
if (_queue.isEmpty) _hasRequestedAnEventLoopCallback = false;
_runTasks();
}
void _runTasks() {
if (_taskQueue.isEmpty)
return; return;
_SchedulerEntry entry = _queue.first; _TaskEntry entry = _taskQueue.first;
if (schedulingStrategy.shouldRunTaskWithPriority(entry.priority)) { if (schedulingStrategy.shouldRunTaskWithPriority(entry.priority)) {
try { try {
(_queue.removeFirst().task)(); (_taskQueue.removeFirst().task)();
} finally { } finally {
if (_queue.isNotEmpty) if (_taskQueue.isNotEmpty)
_wakeNow(); _ensureEventLoopCallback();
} }
} else { } else {
_wakeNextFrame(); // TODO(floitsch): we shouldn't need to request a frame. Just schedule
// an event-loop callback.
_ensureBeginFrameCallback();
} }
} }
int _nextFrameCallbackId = 0; // positive int _nextFrameCallbackId = 0; // positive
Map<int, SchedulerCallback> _transientCallbacks = <int, SchedulerCallback>{}; Map<int, FrameCallback> _transientCallbacks = <int, FrameCallback>{};
final Set<int> _removedIds = new Set<int>(); final Set<int> _removedIds = new Set<int>();
int get transientCallbackCount => _transientCallbacks.length; int get transientCallbackCount => _transientCallbacks.length;
/// Schedules the given frame callback. /// Schedules the given frame callback.
int requestAnimationFrame(SchedulerCallback callback) { ///
/// Adds the given callback to the list of frame-callbacks and ensures that a
/// frame is scheduled.
int scheduleFrameCallback(FrameCallback callback) {
_ensureBeginFrameCallback();
return addFrameCallback(callback);
}
/// Adds a frame callback.
///
/// Frame callbacks are executed at the beginning of a frame (see
/// [handleBeginFrame]).
///
/// The registered callbacks are executed in the order in which they have been
/// registered.
int addFrameCallback(FrameCallback callback) {
_nextFrameCallbackId += 1; _nextFrameCallbackId += 1;
_transientCallbacks[_nextFrameCallbackId] = callback; _transientCallbacks[_nextFrameCallbackId] = callback;
_wakeNextFrame();
return _nextFrameCallbackId; return _nextFrameCallbackId;
} }
/// Cancels the callback of the given [id]. /// Cancels the callback of the given [id].
void cancelAnimationFrame(int id) { ///
/// Removes the given callback from the list of frame callbacks. If a frame
/// has been requested does *not* cancel that request.
void cancelFrameCallbackWithId(int id) {
assert(id > 0); assert(id > 0);
_transientCallbacks.remove(id); _transientCallbacks.remove(id);
_removedIds.add(id); _removedIds.add(id);
} }
final List<SchedulerCallback> _persistentCallbacks = new List<SchedulerCallback>(); final List<FrameCallback> _persistentCallbacks = new List<FrameCallback>();
void addPersistentFrameCallback(SchedulerCallback callback) { /// Adds a persistent frame callback.
///
/// Persistent callbacks are invoked after transient (non-persistent) frame
/// callbacks.
///
/// Does *not* request a new frame. Conceptually, persistent
/// frame-callbacks are thus observers of begin-frame events. Since they are
/// executed after the transient frame-callbacks they can drive the rendering
/// pipeline.
void addPersistentFrameCallback(FrameCallback callback) {
_persistentCallbacks.add(callback); _persistentCallbacks.add(callback);
} }
final List<SchedulerCallback> _postFrameCallbacks = new List<SchedulerCallback>(); final List<FrameCallback> _postFrameCallbacks = new List<FrameCallback>();
/// Schedule a callback for the end of this frame. /// Schedule a callback for the end of this frame.
/// ///
/// If a frame is in progress, the callback will be run just after the main /// Does *not* request a new frame.
/// rendering pipeline has been flushed. In this case, order is preserved (the
/// callbacks are run in registration order).
/// ///
/// If no frame is in progress, it will be called at the start of the next /// The callback is run just after the persistent frame-callbacks (which is
/// frame. In this case, the registration order is not preserved. Callbacks /// when the main rendering pipeline has been flushed). If a frame is
/// are called in an arbitrary order. /// in progress, but post frame-callbacks haven't been executed yet, then the
void requestPostFrameCallback(SchedulerCallback callback) { /// registered callback is still executed during the frame. Otherwise,
/// the registered callback is executed during the next frame.
///
/// The registered callbacks are executed in the order in which they have been
/// registered.
void addPostFrameCallback(FrameCallback callback) {
_postFrameCallbacks.add(callback); _postFrameCallbacks.add(callback);
} }
bool _inFrame = false; bool _isInFrame = false;
void _invokeAnimationCallbacks(Duration timeStamp) { void _invokeTransientFrameCallbacks(Duration timeStamp) {
Timeline.startSync('Animate'); Timeline.startSync('Animate');
assert(_inFrame); assert(_isInFrame);
Map<int, SchedulerCallback> callbacks = _transientCallbacks; Map<int, FrameCallback> callbacks = _transientCallbacks;
_transientCallbacks = new Map<int, SchedulerCallback>(); _transientCallbacks = new Map<int, FrameCallback>();
callbacks.forEach((int id, SchedulerCallback callback) { callbacks.forEach((int id, FrameCallback callback) {
if (!_removedIds.contains(id)) if (!_removedIds.contains(id))
invokeCallback(callback, timeStamp); invokeFrameCallback(callback, timeStamp);
}); });
_removedIds.clear(); _removedIds.clear();
Timeline.finishSync(); Timeline.finishSync();
...@@ -187,35 +223,41 @@ class Scheduler { ...@@ -187,35 +223,41 @@ class Scheduler {
/// Called by the engine to produce a new frame. /// Called by the engine to produce a new frame.
/// ///
/// This function first calls all the callbacks registered by /// This function first calls all the callbacks registered by
/// [requestAnimationFrame], then calls all the callbacks registered by /// [scheduleFrameCallback]/[addFrameCallback], then calls all the callbacks
/// [addPersistentFrameCallback], which typically drive the rendering pipeline, /// registered by [addPersistentFrameCallback], which typically drive the
/// and finally calls the callbacks registered by [requestPostFrameCallback]. /// rendering pipeline, and finally calls the callbacks registered by
void beginFrame(Duration rawTimeStamp) { /// [addPostFrameCallback].
void handleBeginFrame(Duration rawTimeStamp) {
Timeline.startSync('Begin frame'); Timeline.startSync('Begin frame');
assert(!_inFrame); assert(!_isInFrame);
_inFrame = true; _isInFrame = true;
Duration timeStamp = new Duration( Duration timeStamp = new Duration(
microseconds: (rawTimeStamp.inMicroseconds / timeDilation).round()); microseconds: (rawTimeStamp.inMicroseconds / timeDilation).round());
_wakingNextFrame = false; _hasRequestedABeginFrameCallback = false;
_invokeAnimationCallbacks(timeStamp); _invokeTransientFrameCallbacks(timeStamp);
for (SchedulerCallback callback in _persistentCallbacks) for (FrameCallback callback in _persistentCallbacks)
invokeCallback(callback, timeStamp); invokeFrameCallback(callback, timeStamp);
List<SchedulerCallback> localPostFrameCallbacks = List<FrameCallback> localPostFrameCallbacks =
new List<SchedulerCallback>.from(_postFrameCallbacks); new List<FrameCallback>.from(_postFrameCallbacks);
_postFrameCallbacks.clear(); _postFrameCallbacks.clear();
for (SchedulerCallback callback in localPostFrameCallbacks) for (FrameCallback callback in localPostFrameCallbacks)
invokeCallback(callback, timeStamp); invokeFrameCallback(callback, timeStamp);
_inFrame = false; _isInFrame = false;
Timeline.finishSync(); Timeline.finishSync();
// All frame-related callbacks have been executed. Run lower-priority tasks. // All frame-related callbacks have been executed. Run lower-priority tasks.
tick(); _runTasks();
} }
void invokeCallback(SchedulerCallback callback, Duration timeStamp) { /// Invokes the given [callback] with [timestamp] as argument.
///
/// Wraps the callback in a try/catch and forwards any error to
/// [debugSchedulerExceptionHandler], if set. If not set, then simply prints
/// the error.
void invokeFrameCallback(FrameCallback callback, Duration timeStamp) {
assert(callback != null); assert(callback != null);
try { try {
callback(timeStamp); callback(timeStamp);
...@@ -231,28 +273,25 @@ class Scheduler { ...@@ -231,28 +273,25 @@ class Scheduler {
} }
} }
/// Tells the system that the scheduler is awake and should be called as /// Ensures that the scheduler is woken by the event loop.
/// soon a there is time. void _ensureEventLoopCallback() {
void _wakeNow() { if (_hasRequestedAnEventLoopCallback)
if (_wakingNow)
return; return;
_wakingNow = true; Timer.run(handleEventLoopCallback);
Timer.run(() { _hasRequestedAnEventLoopCallback = true;
_wakingNow = false;
tick();
});
} }
// TODO(floitsch): "ensureVisualUpdate" doesn't really fit into the scheduler.
void ensureVisualUpdate() { void ensureVisualUpdate() {
_wakeNextFrame(); _ensureBeginFrameCallback();
} }
/// Schedules a new frame. /// Schedules a new frame.
void _wakeNextFrame() { void _ensureBeginFrameCallback() {
if (_wakingNextFrame) if (_hasRequestedABeginFrameCallback)
return; return;
_wakingNextFrame = true;
ui.window.scheduleFrame(); ui.window.scheduleFrame();
_hasRequestedABeginFrameCallback = true;
} }
} }
......
...@@ -55,7 +55,7 @@ class HeroController extends NavigatorObserver { ...@@ -55,7 +55,7 @@ class HeroController extends NavigatorObserver {
void _checkForHeroQuest() { void _checkForHeroQuest() {
if (_from != null && _to != null && _from != _to) { if (_from != null && _to != null && _from != _to) {
_to.offstage = _to.performance.status != PerformanceStatus.completed; _to.offstage = _to.performance.status != PerformanceStatus.completed;
scheduler.requestPostFrameCallback(_updateQuest); scheduler.addPostFrameCallback(_updateQuest);
} }
} }
......
...@@ -361,7 +361,7 @@ class SpriteBox extends RenderBox { ...@@ -361,7 +361,7 @@ class SpriteBox extends RenderBox {
// Updates // Updates
void _scheduleTick() { void _scheduleTick() {
scheduler.requestAnimationFrame(_tick); scheduler.scheduleFrameCallback(_tick);
} }
void _tick(Duration timeStamp) { void _tick(Duration timeStamp) {
......
...@@ -34,7 +34,8 @@ class WidgetTester { ...@@ -34,7 +34,8 @@ class WidgetTester {
void pump([ Duration duration ]) { void pump([ Duration duration ]) {
if (duration != null) if (duration != null)
async.elapse(duration); async.elapse(duration);
scheduler.beginFrame(new Duration(milliseconds: clock.now().millisecondsSinceEpoch)); scheduler.handleBeginFrame(new Duration(
milliseconds: clock.now().millisecondsSinceEpoch));
async.flushMicrotasks(); async.flushMicrotasks();
} }
......
...@@ -17,7 +17,7 @@ void main() { ...@@ -17,7 +17,7 @@ void main() {
expect(secondCallbackRan, isFalse); expect(secondCallbackRan, isFalse);
expect(timeStamp.inMilliseconds, equals(16)); expect(timeStamp.inMilliseconds, equals(16));
firstCallbackRan = true; firstCallbackRan = true;
scheduler.cancelAnimationFrame(secondId); scheduler.cancelFrameCallbackWithId(secondId);
} }
void secondCallback(Duration timeStamp) { void secondCallback(Duration timeStamp) {
...@@ -27,10 +27,10 @@ void main() { ...@@ -27,10 +27,10 @@ void main() {
secondCallbackRan = true; secondCallbackRan = true;
} }
scheduler.requestAnimationFrame(firstCallback); scheduler.scheduleFrameCallback(firstCallback);
secondId = scheduler.requestAnimationFrame(secondCallback); secondId = scheduler.scheduleFrameCallback(secondCallback);
scheduler.beginFrame(const Duration(milliseconds: 16)); scheduler.handleBeginFrame(const Duration(milliseconds: 16));
expect(firstCallbackRan, isTrue); expect(firstCallbackRan, isTrue);
expect(secondCallbackRan, isFalse); expect(secondCallbackRan, isFalse);
...@@ -38,7 +38,7 @@ void main() { ...@@ -38,7 +38,7 @@ void main() {
firstCallbackRan = false; firstCallbackRan = false;
secondCallbackRan = false; secondCallbackRan = false;
scheduler.beginFrame(const Duration(milliseconds: 32)); scheduler.handleBeginFrame(const Duration(milliseconds: 32));
expect(firstCallbackRan, isFalse); expect(firstCallbackRan, isFalse);
expect(secondCallbackRan, isFalse); expect(secondCallbackRan, isFalse);
......
...@@ -28,17 +28,17 @@ void main() { ...@@ -28,17 +28,17 @@ void main() {
scheduleAddingTask(x); scheduleAddingTask(x);
} }
strategy.allowedPriority = 100; strategy.allowedPriority = 100;
for (int i = 0; i < 3; i++) scheduler.tick(); for (int i = 0; i < 3; i++) scheduler.handleEventLoopCallback();
expect(executedTasks.isEmpty, isTrue); expect(executedTasks.isEmpty, isTrue);
strategy.allowedPriority = 50; strategy.allowedPriority = 50;
for (int i = 0; i < 3; i++) scheduler.tick(); for (int i = 0; i < 3; i++) scheduler.handleEventLoopCallback();
expect(executedTasks.length, equals(1)); expect(executedTasks.length, equals(1));
expect(executedTasks.single, equals(80)); expect(executedTasks.single, equals(80));
executedTasks.clear(); executedTasks.clear();
strategy.allowedPriority = 20; strategy.allowedPriority = 20;
for (int i = 0; i < 3; i++) scheduler.tick(); for (int i = 0; i < 3; i++) scheduler.handleEventLoopCallback();
expect(executedTasks.length, equals(2)); expect(executedTasks.length, equals(2));
expect(executedTasks[0], equals(23)); expect(executedTasks[0], equals(23));
expect(executedTasks[1], equals(23)); expect(executedTasks[1], equals(23));
...@@ -48,21 +48,21 @@ void main() { ...@@ -48,21 +48,21 @@ void main() {
scheduleAddingTask(19); scheduleAddingTask(19);
scheduleAddingTask(5); scheduleAddingTask(5);
scheduleAddingTask(97); scheduleAddingTask(97);
for (int i = 0; i < 3; i++) scheduler.tick(); for (int i = 0; i < 3; i++) scheduler.handleEventLoopCallback();
expect(executedTasks.length, equals(2)); expect(executedTasks.length, equals(2));
expect(executedTasks[0], equals(99)); expect(executedTasks[0], equals(99));
expect(executedTasks[1], equals(97)); expect(executedTasks[1], equals(97));
executedTasks.clear(); executedTasks.clear();
strategy.allowedPriority = 10; strategy.allowedPriority = 10;
for (int i = 0; i < 3; i++) scheduler.tick(); for (int i = 0; i < 3; i++) scheduler.handleEventLoopCallback();
expect(executedTasks.length, equals(2)); expect(executedTasks.length, equals(2));
expect(executedTasks[0], equals(19)); expect(executedTasks[0], equals(19));
expect(executedTasks[1], equals(11)); expect(executedTasks[1], equals(11));
executedTasks.clear(); executedTasks.clear();
strategy.allowedPriority = 1; strategy.allowedPriority = 1;
for (int i = 0; i < 4; i++) scheduler.tick(); for (int i = 0; i < 4; i++) scheduler.handleEventLoopCallback();
expect(executedTasks.length, equals(3)); expect(executedTasks.length, equals(3));
expect(executedTasks[0], equals(5)); expect(executedTasks[0], equals(5));
expect(executedTasks[1], equals(3)); expect(executedTasks[1], equals(3));
...@@ -70,7 +70,7 @@ void main() { ...@@ -70,7 +70,7 @@ void main() {
executedTasks.clear(); executedTasks.clear();
strategy.allowedPriority = 0; strategy.allowedPriority = 0;
scheduler.tick(); scheduler.handleEventLoopCallback();
expect(executedTasks.length, equals(1)); expect(executedTasks.length, equals(1));
expect(executedTasks[0], equals(0)); expect(executedTasks[0], equals(0));
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment