Sky has three task queues, named idle, frame, and nextFrame.
When a task is run, it has a time budget, and if the time budget is exceeded, then a catchable DeadlineExceededException exception is fired.
class DeadlineExceededException implements Exception { }
There is a method you can use that guards your code against these exceptions:
typedef void Callback(); external guardAgainstDeadlineExceptions(Callback callback); // runs callback. // if the time budget for the _task_ expires while the callback is // running, the callback isn't interrupted, but the method will throw // an exception once the callback returns.
When Sky is to process a task queue until a particular time, with a queue relevant task queue, bits filter bits, a time particular time, and an idle rule which is either “sleep” or “abort”, it must run the following steps:
When Sky is to drain a task queue for a specified time, with a queue relevant task queue, bits filter bits, and a duration budget, it must run the following steps:
Sky's run loop consists of running the following, at 120Hz (each loop takes 8.333ms):
Drain the frame task queue, with bits application.frameTaskBits
, for 1ms.
Create a task that does the following, then run it with a budget of 1ms:
If an exception is thrown by this, then the RenderNode tree will continue to not quite match the element tree, which is fine.
If there are no tasks on the idle task queue with bits LayoutKind
, create a task that tells the root node to layout if it has needsLayout or descendantNeedsLayout, mark that with priority 0 and bits LayoutKind
, and add it to the idle task queue.
Process the idle task queue, with bits LayoutKind
, with a target time of t-1ms, where t is the time at which we have to send the frame to the GPU, and with an idle rule of “abort”.
Create a task that does the following, then run it with a budget of 1ms:
If there are no RenderNodes that need paint, abort.
Call the paint()
callback of the RenderNode that was least recently marked as needing paint, catching any exceptions other than DeadlineExceededException exceptions.
Jump to step 1.
If an exception is thrown by this, then some RenderNode objects will be out-of-date during the paint.
Send frame to GPU.
Replace the frame queue with the nextFrame queue, and let the nextFrame queue be an empty queue.
Process the idle task queue, with bits application.idleTaskBits
, with a target time of t, where t is the time at which we have to start the next frame's layout and paint computations, and with an idle rule of “sleep”.
TODO(ianh): Update the timings above to have some relationship to reality.
TODO(ianh): Define an API so that the application can adjust the budgets.
Tasks scheduled by futures get the priority and task kind bits from the task they are scheduled from.
int IdlePriority = 0; // tasks that can be delayed arbitrarily int FutureLayoutPriority = 1000; // async-layout tasks int AnimationPriority = 3000; // animation-related tasks int InputPriority = 4000; // input events int ScrollPriority = 5000; // framework-fired events for scrolling // possible idle queue task bits int IdleKind = 0x01; // tasks that should run during the idle loop int LayoutKind = 0x02; // tasks that should run during layout int TouchSafeKind = 0x04; // tasks that should keep running while there is a pointer down int idleTaskBits = IdleKind; // tasks must have all these bits to run during idle loop int layoutTaskBits = LayoutKind; // tasks must have all these bits to run during layout // possible frame queue task bits // (there are none at this time) int frameTaskBits = 0x00; // tasks must have all these bits to run during the frame loop