Bug 1592170 [wpt PR 19965] - Fix default tabIndex IDL attribute values, a=testonly
[gecko.git] / gfx / docs / AsyncPanZoom.rst
blob83990b2565191bea25e4400657a1efcc1ba87302
1 .. _apz:
3 Asynchronous Panning and Zooming
4 ================================
6 **This document is a work in progress. Some information may be missing
7 or incomplete.**
9 .. image:: AsyncPanZoomArchitecture.png
11 Goals
12 -----
14 We need to be able to provide a visual response to user input with
15 minimal latency. In particular, on devices with touch input, content
16 must track the finger exactly while panning, or the user experience is
17 very poor. According to the UX team, 120ms is an acceptable latency
18 between user input and response.
20 Context and surrounding architecture
21 ------------------------------------
23 The fundamental problem we are trying to solve with the Asynchronous
24 Panning and Zooming (APZ) code is that of responsiveness. By default,
25 web browsers operate in a “game loop” that looks like this:
29        while true:
30            process input
31            do computations
32            repaint content
33            display repainted content
35 In browsers the “do computation” step can be arbitrarily expensive
36 because it can involve running event handlers in web content. Therefore,
37 there can be an arbitrary delay between the input being received and the
38 on-screen display getting updated.
40 Responsiveness is always good, and with touch-based interaction it is
41 even more important than with mouse or keyboard input. In order to
42 ensure responsiveness, we split the “game loop” model of the browser
43 into a multithreaded variant which looks something like this:
47        Thread 1 (compositor thread)
48        while true:
49            receive input
50            send a copy of input to thread 2
51            adjust painted content based on input
52            display adjusted painted content
53        
54        Thread 2 (main thread)
55        while true:
56            receive input from thread 1
57            do computations
58            repaint content
59            update the copy of painted content in thread 1
61 This multithreaded model is called off-main-thread compositing (OMTC),
62 because the compositing (where the content is displayed on-screen)
63 happens on a separate thread from the main thread. Note that this is a
64 very very simplified model, but in this model the “adjust painted
65 content based on input” is the primary function of the APZ code.
67 The “painted content” is stored on a set of “layers”, that are
68 conceptually double-buffered. That is, when the main thread does its
69 repaint, it paints into one set of layers (the “client” layers). The
70 update that is sent to the compositor thread copies all the changes from
71 the client layers into another set of layers that the compositor holds.
72 These layers are called the “shadow” layers or the “compositor” layers.
73 The compositor in theory can continuously composite these shadow layers
74 to the screen while the main thread is busy doing other things and
75 painting a new set of client layers.
77 The APZ code takes the input events that are coming in from the hardware
78 and uses them to figure out what the user is trying to do (e.g. pan the
79 page, zoom in). It then expresses this user intention in the form of
80 translation and/or scale transformation matrices. These transformation
81 matrices are applied to the shadow layers at composite time, so that
82 what the user sees on-screen reflects what they are trying to do as
83 closely as possible.
85 Technical overview
86 ------------------
88 As per the heavily simplified model described above, the fundamental
89 purpose of the APZ code is to take input events and produce
90 transformation matrices. This section attempts to break that down and
91 identify the different problems that make this task non-trivial.
93 Checkerboarding
94 ~~~~~~~~~~~~~~~
96 The content area that is painted and stored in a shadow layer is called
97 the “displayport”. The APZ code is responsible for determining how large
98 the displayport should be. On the one hand, we want the displayport to
99 be as large as possible. At the very least it needs to be larger than
100 what is visible on-screen, because otherwise, as soon as the user pans,
101 there will be some unpainted area of the page exposed. However, we
102 cannot always set the displayport to be the entire page, because the
103 page can be arbitrarily long and this would require an unbounded amount
104 of memory to store. Therefore, a good displayport size is one that is
105 larger than the visible area but not so large that it is a huge drain on
106 memory. Because the displayport is usually smaller than the whole page,
107 it is always possible for the user to scroll so fast that they end up in
108 an area of the page outside the displayport. When this happens, they see
109 unpainted content; this is referred to as “checkerboarding”, and we try
110 to avoid it where possible.
112 There are many possible ways to determine what the displayport should be
113 in order to balance the tradeoffs involved (i.e. having one that is too
114 big is bad for memory usage, and having one that is too small results in
115 excessive checkerboarding). Ideally, the displayport should cover
116 exactly the area that we know the user will make visible. Although we
117 cannot know this for sure, we can use heuristics based on current
118 panning velocity and direction to ensure a reasonably-chosen displayport
119 area. This calculation is done in the APZ code, and a new desired
120 displayport is frequently sent to the main thread as the user is panning
121 around.
123 Multiple layers
124 ~~~~~~~~~~~~~~~
126 Consider, for example, a scrollable page that contains an iframe which
127 itself is scrollable. The iframe can be scrolled independently of the
128 top-level page, and we would like both the page and the iframe to scroll
129 responsively. This means that we want independent asynchronous panning
130 for both the top-level page and the iframe. In addition to iframes,
131 elements that have the overflow:scroll CSS property set are also
132 scrollable, and also end up on separate scrollable layers. In the
133 general case, the layers are arranged in a tree structure, and so within
134 the APZ code we have a matching tree of AsyncPanZoomController (APZC)
135 objects, one for each scrollable layer. To manage this tree of APZC
136 instances, we have a single APZCTreeManager object. Each APZC is
137 relatively independent and handles the scrolling for its associated
138 layer, but there are some cases in which they need to interact; these
139 cases are described in the sections below.
141 Hit detection
142 ~~~~~~~~~~~~~
144 Consider again the case where we have a scrollable page that contains an
145 iframe which itself is scrollable. As described above, we will have two
146 APZC instances - one for the page and one for the iframe. When the user
147 puts their finger down on the screen and moves it, we need to do some
148 sort of hit detection in order to determine whether their finger is on
149 the iframe or on the top-level page. Based on where their finger lands,
150 the appropriate APZC instance needs to handle the input. This hit
151 detection is also done in the APZCTreeManager, as it has the necessary
152 information about the sizes and positions of the layers. Currently this
153 hit detection is not perfect, as it uses rects and does not account for
154 things like rounded corners and opacity.
156 Also note that for some types of input (e.g. when the user puts two
157 fingers down to do a pinch) we do not want the input to be “split”
158 across two different APZC instances. In the case of a pinch, for
159 example, we find a “common ancestor” APZC instance - one that is
160 zoomable and contains all of the touch input points, and direct the
161 input to that APZC instance.
163 Scroll Handoff
164 ~~~~~~~~~~~~~~
166 Consider yet again the case where we have a scrollable page that
167 contains an iframe which itself is scrollable. Say the user scrolls the
168 iframe so that it reaches the bottom. If the user continues panning on
169 the iframe, the expectation is that the top-level page will start
170 scrolling. However, as discussed in the section on hit detection, the
171 APZC instance for the iframe is separate from the APZC instance for the
172 top-level page. Thus, we need the two APZC instances to communicate in
173 some way such that input events on the iframe result in scrolling on the
174 top-level page. This behaviour is referred to as “scroll handoff” (or
175 “fling handoff” in the case where analogous behaviour results from the
176 scrolling momentum of the page after the user has lifted their finger).
178 Input event untransformation
179 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
181 The APZC architecture by definition results in two copies of a “scroll
182 position” for each scrollable layer. There is the original copy on the
183 main thread that is accessible to web content and the layout and
184 painting code. And there is a second copy on the compositor side, which
185 is updated asynchronously based on user input, and corresponds to what
186 the user visually sees on the screen. Although these two copies may
187 diverge temporarily, they are reconciled periodically. In particular,
188 they diverge while the APZ code is performing an async pan or zoom
189 action on behalf of the user, and are reconciled when the APZ code
190 requests a repaint from the main thread.
192 Because of the way input events are stored, this has some unfortunate
193 consequences. Input events are stored relative to the device screen - so
194 if the user touches at the same physical spot on the device, the same
195 input events will be delivered regardless of the content scroll
196 position. When the main thread receives a touch event, it combines that
197 with the content scroll position in order to figure out what DOM element
198 the user touched. However, because we now have two different scroll
199 positions, this process may not work perfectly. A concrete example
200 follows:
202 Consider a device with screen size 600 pixels tall. On this device, a
203 user is viewing a document that is 1000 pixels tall, and that is
204 scrolled down by 200 pixels. That is, the vertical section of the
205 document from 200px to 800px is visible. Now, if the user touches a
206 point 100px from the top of the physical display, the hardware will
207 generate a touch event with y=100. This will get sent to the main
208 thread, which will add the scroll position (200) and get a
209 document-relative touch event with y=300. This new y-value will be used
210 in hit detection to figure out what the user touched. If the document
211 had a absolute-positioned div at y=300, then that would receive the
212 touch event.
214 Now let us add some async scrolling to this example. Say that the user
215 additionally scrolls the document by another 10 pixels asynchronously
216 (i.e. only on the compositor thread), and then does the same touch
217 event. The same input event is generated by the hardware, and as before,
218 the document will deliver the touch event to the div at y=300. However,
219 visually, the document is scrolled by an additional 10 pixels so this
220 outcome is wrong. What needs to happen is that the APZ code needs to
221 intercept the touch event and account for the 10 pixels of asynchronous
222 scroll. Therefore, the input event with y=100 gets converted to y=110 in
223 the APZ code before being passed on to the main thread. The main thread
224 then adds the scroll position it knows about and determines that the
225 user touched at a document-relative position of y=310.
227 Analogous input event transformations need to be done for horizontal
228 scrolling and zooming.
230 Content independently adjusting scrolling
231 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
233 As described above, there are two copies of the scroll position in the
234 APZ architecture - one on the main thread and one on the compositor
235 thread. Usually for architectures like this, there is a single “source
236 of truth” value and the other value is simply a copy. However, in this
237 case that is not easily possible to do. The reason is that both of these
238 values can be legitimately modified. On the compositor side, the input
239 events the user is triggering modify the scroll position, which is then
240 propagated to the main thread. However, on the main thread, web content
241 might be running Javascript code that programmatically sets the scroll
242 position (via window.scrollTo, for example). Scroll changes driven from
243 the main thread are just as legitimate and need to be propagated to the
244 compositor thread, so that the visual display updates in response.
246 Because the cross-thread messaging is asynchronous, reconciling the two
247 types of scroll changes is a tricky problem. Our design solves this
248 using various flags and generation counters. The general heuristic we
249 have is that content-driven scroll position changes (e.g. scrollTo from
250 JS) are never lost. For instance, if the user is doing an async scroll
251 with their finger and content does a scrollTo in the middle, then some
252 of the async scroll would occur before the “jump” and the rest after the
253 “jump”.
255 Content preventing default behaviour of input events
256 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
258 Another problem that we need to deal with is that web content is allowed
259 to intercept touch events and prevent the “default behaviour” of
260 scrolling. This ability is defined in web standards and is
261 non-negotiable. Touch event listeners in web content are allowed call
262 preventDefault() on the touchstart or first touchmove event for a touch
263 point; doing this is supposed to “consume” the event and prevent
264 touch-based panning. As we saw in a previous section, the input event
265 needs to be untransformed by the APZ code before it can be delivered to
266 content. But, because of the preventDefault problem, we cannot fully
267 process the touch event in the APZ code until content has had a chance
268 to handle it. Web browsers in general solve this problem by inserting a
269 delay of up to 300ms before processing the input - that is, web content
270 is allowed up to 300ms to process the event and call preventDefault on
271 it. If web content takes longer than 300ms, or if it completes handling
272 of the event without calling preventDefault, then the browser
273 immediately starts processing the events.
275 The way the APZ implementation deals with this is that upon receiving a
276 touch event, it immediately returns an untransformed version that can be
277 dispatched to content. It also schedules a 400ms timeout (600ms on
278 Android) during which content is allowed to prevent scrolling. There is
279 an API that allows the main-thread event dispatching code to notify the
280 APZ as to whether or not the default action should be prevented. If the
281 APZ content response timeout expires, or if the main-thread event
282 dispatching code notifies the APZ of the preventDefault status, then the
283 APZ continues with the processing of the events (which may involve
284 discarding the events).
286 The touch-action CSS property from the pointer-events spec is intended
287 to allow eliminating this 400ms delay in many cases (although for
288 backwards compatibility it will still be needed for a while). Note that
289 even with touch-action implemented, there may be cases where the APZ
290 code does not know the touch-action behaviour of the point the user
291 touched. In such cases, the APZ code will still wait up to 400ms for the
292 main thread to provide it with the touch-action behaviour information.
294 Technical details
295 -----------------
297 This section describes various pieces of the APZ code, and goes into
298 more specific detail on APIs and code than the previous sections. The
299 primary purpose of this section is to help people who plan on making
300 changes to the code, while also not going into so much detail that it
301 needs to be updated with every patch.
303 Overall flow of input events
304 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
306 This section describes how input events flow through the APZ code.
308 1.  Input events arrive from the hardware/widget code into the APZ via
309     APZCTreeManager::ReceiveInputEvent. The thread that invokes this is
310     called the input thread, and may or may not be the same as the Gecko
311     main thread.
312 2.  Conceptually the first thing that the APZCTreeManager does is to
313     associate these events with “input blocks”. An input block is a set
314     of events that share certain properties, and generally are intended
315     to represent a single gesture. For example with touch events, all
316     events following a touchstart up to but not including the next
317     touchstart are in the same block. All of the events in a given block
318     will go to the same APZC instance and will either all be processed
319     or all be dropped.
320 3.  Using the first event in the input block, the APZCTreeManager does a
321     hit-test to see which APZC it hits. This hit-test uses the event
322     regions populated on the layers, which may be larger than the true
323     hit area of the layer. If no APZC is hit, the events are discarded
324     and we jump to step 6. Otherwise, the input block is tagged with the
325     hit APZC as a tentative target and put into a global APZ input
326     queue.
329     i.  If the input events landed outside the dispatch-to-content event
330         region for the layer, any available events in the input block
331         are processed. These may trigger behaviours like scrolling or
332         tap gestures.
333     ii. If the input events landed inside the dispatch-to-content event
334         region for the layer, the events are left in the queue and a
335         400ms timeout is initiated. If the timeout expires before step 9
336         is completed, the APZ assumes the input block was not cancelled
337         and the tentative target is correct, and processes them as part
338         of step 10.
340 5.  The call stack unwinds back to APZCTreeManager::ReceiveInputEvent,
341     which does an in-place modification of the input event so that any
342     async transforms are removed.
343 6.  The call stack unwinds back to the widget code that called
344     ReceiveInputEvent. This code now has the event in the coordinate
345     space Gecko is expecting, and so can dispatch it to the Gecko main
346     thread.
347 7.  Gecko performs its own usual hit-testing and event dispatching for
348     the event. As part of this, it records whether any touch listeners
349     cancelled the input block by calling preventDefault(). It also
350     activates inactive scrollframes that were hit by the input events.
351 8.  The call stack unwinds back to the widget code, which sends two
352     notifications to the APZ code on the input thread. The first
353     notification is via APZCTreeManager::ContentReceivedInputBlock, and
354     informs the APZ whether the input block was cancelled. The second
355     notification is via APZCTreeManager::SetTargetAPZC, and informs the
356     APZ of the results of the Gecko hit-test during event dispatch. Note
357     that Gecko may report that the input event did not hit any
358     scrollable frame at all. The SetTargetAPZC notification happens only
359     once per input block, while the ContentReceivedInputBlock
360     notification may happen once per block, or multiple times per block,
361     depending on the input type.
364     i.   If the events were processed as part of step 4(i), the
365          notifications from step 8 are ignored and step 10 is skipped.
366     ii.  If events were queued as part of step 4(ii), and steps 5-8 take
367          less than 400ms, the arrival of both notifications from step 8
368          will mark the input block ready for processing.
369     iii. If events were queued as part of step 4(ii), but steps 5-8 take
370          longer than 400ms, the notifications from step 8 will be
371          ignored and step 10 will already have happened.
373 10. If events were queued as part of step 4(ii) they are now either
374     processed (if the input block was not cancelled and Gecko detected a
375     scrollframe under the input event, or if the timeout expired) or
376     dropped (all other cases). Note that the APZC that processes the
377     events may be different at this step than the tentative target from
378     step 3, depending on the SetTargetAPZC notification. Processing the
379     events may trigger behaviours like scrolling or tap gestures.
381 If the CSS touch-action property is enabled, the above steps are
382 modified as follows: \* In step 4, the APZC also requires the allowed
383 touch-action behaviours for the input event. This might have been
384 determined as part of the hit-test in APZCTreeManager; if not, the
385 events are queued. \* In step 6, the widget code determines the content
386 element at the point under the input element, and notifies the APZ code
387 of the allowed touch-action behaviours. This notification is sent via a
388 call to APZCTreeManager::SetAllowedTouchBehavior on the input thread. \*
389 In step 9(ii), the input block will only be marked ready for processing
390 once all three notifications arrive.
392 Threading considerations
393 ^^^^^^^^^^^^^^^^^^^^^^^^
395 The bulk of the input processing in the APZ code happens on what we call
396 “the input thread”. In practice the input thread could be the Gecko main
397 thread, the compositor thread, or some other thread. There are obvious
398 downsides to using the Gecko main thread - that is, “asynchronous”
399 panning and zooming is not really asynchronous as input events can only
400 be processed while Gecko is idle. In an e10s environment, using the
401 Gecko main thread of the chrome process is acceptable, because the code
402 running in that process is more controllable and well-behaved than
403 arbitrary web content. Using the compositor thread as the input thread
404 could work on some platforms, but may be inefficient on others. For
405 example, on Android (Fennec) we receive input events from the system on
406 a dedicated UI thread. We would have to redispatch the input events to
407 the compositor thread if we wanted to the input thread to be the same as
408 the compositor thread. This introduces a potential for higher latency,
409 particularly if the compositor does any blocking operations - blocking
410 SwapBuffers operations, for example. As a result, the APZ code itself
411 does not assume that the input thread will be the same as the Gecko main
412 thread or the compositor thread.
414 Active vs. inactive scrollframes
415 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
417 The number of scrollframes on a page is potentially unbounded. However,
418 we do not want to create a separate layer for each scrollframe right
419 away, as this would require large amounts of memory. Therefore,
420 scrollframes as designated as either “active” or “inactive”. Active
421 scrollframes are the ones that do have their contents put on a separate
422 layer (or set of layers), and inactive ones do not.
424 Consider a page with a scrollframe that is initially inactive. When
425 layout generates the layers for this page, the content of the
426 scrollframe will be flattened into some other PaintedLayer (call it P).
427 The layout code also adds the area (or bounding region in case of weird
428 shapes) of the scrollframe to the dispatch-to-content region of P.
430 When the user starts interacting with that content, the hit-test in the
431 APZ code finds the dispatch-to-content region of P. The input block
432 therefore has a tentative target of P when it goes into step 4(ii) in
433 the flow above. When gecko processes the input event, it must detect the
434 inactive scrollframe and activate it, as part of step 7. Finally, the
435 widget code sends the SetTargetAPZC notification in step 8 to notify the
436 APZ that the input block should really apply to this new layer. The
437 issue here is that the layer transaction containing the new layer must
438 reach the compositor and APZ before the SetTargetAPZC notification. If
439 this does not occur within the 400ms timeout, the APZ code will be
440 unable to update the tentative target, and will continue to use P for
441 that input block. Input blocks that start after the layer transaction
442 will get correctly routed to the new layer as there will now be a layer
443 and APZC instance for the active scrollframe.
445 This model implies that when the user initially attempts to scroll an
446 inactive scrollframe, it may end up scrolling an ancestor scrollframe.
447 (This is because in the absence of the SetTargetAPZC notification, the
448 input events will get applied to the closest ancestor scrollframe’s
449 APZC.) Only after the round-trip to the gecko thread is complete is
450 there a layer for async scrolling to actually occur on the scrollframe
451 itself. At that point the scrollframe will start receiving new input
452 blocks and will scroll normally.
454 Threading / Locking Overview
455 ----------------------------
457 Threads
458 ~~~~~~~
460 There are three threads relevant to APZ: the **controller thread**,
461 the **updater thread**, and the **sampler thread**. This table lists
462 which threads play these roles on each platform / configuration:
464 ===================== ========== =========== ============= ============== ========== =============
465 APZ Thread Name       Desktop    Desktop+GPU Desktop+WR    Desktop+WR+GPU Android    Android+WR
466 ===================== ========== =========== ============= ============== ========== =============
467 **controller thread** UI main    GPU main    UI main       GPU main       Java UI    Java UI
468 **updater thread**    Compositor Compositor  SceneBuilder  SceneBuilder   Compositor SceneBuilder
469 **sampler thread**    Compositor Compositor  RenderBackend RenderBackend  Compositor RenderBackend
470 ===================== ========== =========== ============= ============== ========== =============
472 Locks
473 ~~~~~
475 There are also a number of locks used in APZ code:
477 ================== ==============================
478 Lock type          How many instances
479 ================== ==============================
480 APZ tree lock      one per APZCTreeManager
481 APZC map lock      one per APZCTreeManager
482 APZC instance lock one per AsyncPanZoomController
483 APZ test lock      one per APZCTreeManager
484 ================== ==============================
486 Thread / Lock Ordering
487 ~~~~~~~~~~~~~~~~~~~~~~
489 To avoid deadlocks, the threads and locks have a global **ordering**
490 which must be respected.
492 Respecting the ordering means the following:
494 - Let "A < B" denote that A occurs earlier than B in the ordering
495 - Thread T may only acquire lock L, if T < L
496 - A thread may only acquire lock L2 while holding lock L1, if L1 < L2
497 - A thread may only block on a response from another thread T while holding a lock L, if L < T
499 **The lock ordering is as follows**:
501 1. UI main
502 2. GPU main              (only if GPU enabled)
503 3. Compositor thread
504 4. SceneBuilder thread   (only if WR enabled)
505 5. **APZ tree lock**
506 6. RenderBackend thread  (only if WR enabled)
507 7. **APZC map lock**
508 8. **APZC instance lock**
509 9. **APZ test lock**
511 Example workflows
512 ^^^^^^^^^^^^^^^^^
514 Here are some example APZ workflows. Observe how they all obey
515 the global thread/lock ordering. Feel free to add others:
517 - **Input handling** (in WR+GPU) case: UI main -> GPU main -> APZ tree lock -> RenderBackend thread
518 - **Sync messages** in ``PComposiorBridge.ipdl``: UI main thread -> Compositor thread
519 - **GetAPZTestData**: Compositor thread -> SceneBuilder thread -> test lock
520 - **Scene swap**: SceneBuilder thread -> APZ tree lock -> RenderBackend thread
521 - **Updating hit-testing tree**: SceneBuilder thread -> APZ tree lock -> APZC instance lock
522 - **Updating APZC map**: SceneBuilder thread -> APZ tree lock -> APZC map lock
523 - **Sampling and animation deferred tasks** [1]_: RenderBackend thread -> APZC map lock -> APZC instance lock
524 - **Advancing animations**: RenderBackend thread -> APZC instance lock
526 .. [1] It looks like there are two deferred tasks that actually need the tree lock,
527    ``AsyncPanZoomController::HandleSmoothScrollOverscroll`` and
528    ``AsyncPanZoomController::HandleFlingOverscroll``. We should be able to rewrite
529    these to use the map lock instead of the tree lock.
530    This will allow us to continue running the deferred tasks on the sampler
531    thread rather than having to bounce them to another thread.