| OLD | NEW |
| (Empty) |
| 1 Sky's Run Loop | |
| 2 ============== | |
| 3 | |
| 4 Sky has three task queues, named idle, frame, and nextFrame. | |
| 5 | |
| 6 When a task is run, it has a time budget, and if the time budget is | |
| 7 exceeded, then a catchable DeadlineExceededException exception is | |
| 8 fired. | |
| 9 | |
| 10 ```dart | |
| 11 class DeadlineExceededException implements Exception { } | |
| 12 ``` | |
| 13 | |
| 14 There is a method you can use that guards your code against these | |
| 15 exceptions: | |
| 16 | |
| 17 ```dart | |
| 18 typedef void Callback(); | |
| 19 external guardAgainstDeadlineExceptions(Callback callback); | |
| 20 // runs callback. | |
| 21 // if the time budget for the _task_ expires while the callback is | |
| 22 // running, the callback isn't interrupted, but the method will throw | |
| 23 // an exception once the callback returns. | |
| 24 ``` | |
| 25 | |
| 26 When Sky is to *process a task queue until a particular time*, with a | |
| 27 queue *relevant task queue*, bits *filter bits*, a time | |
| 28 *particular time*, and an *idle rule* which is either "sleep" or | |
| 29 "abort", it must run the following steps: | |
| 30 | |
| 31 1. Let *remaining time* be the time until the given *particular time*. | |
| 32 2. If *remaining time* is less than or equal to zero, exit this | |
| 33 algorithm. | |
| 34 3. Let *task list* be the list of tasks in the *relevant task queue* | |
| 35 that have bits that, when 'and'ed with *filter bits*, are equal to | |
| 36 *filter bits*, whose required budget is less than or equal to | |
| 37 *remaining time*; and whose due time, if any, has been reached. | |
| 38 4. If *task list* is empty, then if *idle rule* is "sleep" then return | |
| 39 to step 1, otherwise, exit this algorithm. | |
| 40 5. Sort *task list* by the priority of each task, highest first. | |
| 41 6. Remove the top task from *task list* from the *relevant task | |
| 42 queue*, and let that be *selected task*. | |
| 43 7. Run *selected task*, with a budget of *remaining time* or 1ms, | |
| 44 whichever is shorter. | |
| 45 8. Return to step 1. | |
| 46 | |
| 47 When Sky is to *drain a task queue for a specified time*, with a queue | |
| 48 *relevant task queue*, bits *filter bits*, and a duration *budget*, it | |
| 49 must run the following steps: | |
| 50 | |
| 51 2. Let *task list* be the list of tasks in the *relevant task queue* | |
| 52 that have bits that, when 'or'ed with *filter bits*, are non-zero; | |
| 53 and whose required budget is less than or equal to *budget*. | |
| 54 4. If *task list* is empty, then exit. | |
| 55 5. Sort *task list* by the priority of each task, highest first. | |
| 56 6. Remove the top task from *task list* from the *relevant task | |
| 57 queue*, and let that be *selected task*. | |
| 58 7. Run *selected task*, with a budget of *budget*. | |
| 59 8. Decrease *budget* with the amount of time that *selected task* took | |
| 60 to run. | |
| 61 9. If *selected task* threw an uncaught DeadlineExceededException | |
| 62 exception, then cancel all the tasks in *relevant task queue*. | |
| 63 Otherwise, return to step 2. | |
| 64 | |
| 65 Sky's run loop consists of running the following, at 120Hz (each loop | |
| 66 takes 8.333ms): | |
| 67 | |
| 68 1. *Drain* the *frame task queue*, with bits | |
| 69 `application.frameTaskBits`, for 1ms. | |
| 70 | |
| 71 2. Create a task that does the following, then run it with a budget of | |
| 72 1ms: | |
| 73 | |
| 74 1. Update the render tree, including calling childAdded(), | |
| 75 childRemoved(), and getLayoutManager() as needed, catching any | |
| 76 exceptions other than DeadlineExceededException exceptions. | |
| 77 | |
| 78 If an exception is thrown by this, then the RenderNode tree will | |
| 79 continue to not quite match the element tree, which is fine. | |
| 80 | |
| 81 3. If there are no tasks on the *idle task queue* with bits | |
| 82 `LayoutKind`, create a task that tells the root node to layout if | |
| 83 it has needsLayout or descendantNeedsLayout, mark that with | |
| 84 priority 0 and bits `LayoutKind`, and add it to the *idle task | |
| 85 queue*. | |
| 86 | |
| 87 4. *Process* the *idle task queue*, with bits `LayoutKind`, with a | |
| 88 target time of t-1ms, where t is the time at which we have to send | |
| 89 the frame to the GPU, and with an *idle rule* of "abort". | |
| 90 | |
| 91 5. Create a task that does the following, then run it with a budget of | |
| 92 1ms: | |
| 93 | |
| 94 1. If there are no RenderNodes that need paint, abort. | |
| 95 | |
| 96 2. Call the `paint()` callback of the RenderNode that was least | |
| 97 recently marked as needing paint, catching any exceptions other | |
| 98 than DeadlineExceededException exceptions. | |
| 99 | |
| 100 3. Jump to step 1. | |
| 101 | |
| 102 If an exception is thrown by this, then some RenderNode objects | |
| 103 will be out-of-date during the paint. | |
| 104 | |
| 105 6. Send frame to GPU. | |
| 106 | |
| 107 7. Replace the frame queue with the nextFrame queue, and let the | |
| 108 nextFrame queue be an empty queue. | |
| 109 | |
| 110 8. *Process* the *idle task queue*, with bits | |
| 111 `application.idleTaskBits`, with a target time of t, where t is the | |
| 112 time at which we have to start the next frame's layout and paint | |
| 113 computations, and with an *idle rule* of "sleep". | |
| 114 | |
| 115 TODO(ianh): Update the timings above to have some relationship to | |
| 116 reality. | |
| 117 | |
| 118 TODO(ianh): Define an API so that the application can adjust the | |
| 119 budgets. | |
| 120 | |
| 121 Task kinds and priorities | |
| 122 ------------------------- | |
| 123 | |
| 124 Tasks scheduled by futures get the priority and task kind bits from | |
| 125 the task they are scheduled from. | |
| 126 | |
| 127 ```dart | |
| 128 int IdlePriority = 0; // tasks that can be delayed arbitrarily | |
| 129 int FutureLayoutPriority = 1000; // async-layout tasks | |
| 130 int AnimationPriority = 3000; // animation-related tasks | |
| 131 int InputPriority = 4000; // input events | |
| 132 int ScrollPriority = 5000; // framework-fired events for scrolling | |
| 133 | |
| 134 // possible idle queue task bits | |
| 135 int IdleKind = 0x01; // tasks that should run during the idle loop | |
| 136 int LayoutKind = 0x02; // tasks that should run during layout | |
| 137 int TouchSafeKind = 0x04; // tasks that should keep running while there is a poi
nter down | |
| 138 int idleTaskBits = IdleKind; // tasks must have all these bits to run during idl
e loop | |
| 139 int layoutTaskBits = LayoutKind; // tasks must have all these bits to run during
layout | |
| 140 | |
| 141 // possible frame queue task bits | |
| 142 // (there are none at this time) | |
| 143 int frameTaskBits = 0x00; // tasks must have all these bits to run during the fr
ame loop | |
| 144 ``` | |
| OLD | NEW |