tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Silk.rst (25128B)


      1 Silk Overview
      2 ==========================
      3 
      4 .. image:: SilkArchitecture.png
      5 
      6 Architecture
      7 ------------
      8 
      9 Our current architecture is to align three components to hardware vsync
     10 timers:
     11 
     12 1. Compositor
     13 2. RefreshDriver / Painting
     14 3. Input Events
     15 
     16 The flow of our rendering engine is as follows:
     17 
     18 1. Hardware Vsync event occurs on an OS specific *Hardware Vsync Thread*
     19   on a per monitor basis.
     20 2. The *Hardware Vsync Thread* attached to the monitor notifies the
     21   ``CompositorVsyncDispatchers`` and ``VsyncDispatcher``.
     22 3. For every Firefox window on the specific monitor, notify a
     23   ``CompositorVsyncDispatcher``. The ``CompositorVsyncDispatcher`` is
     24   specific to one window.
     25 4. The ``CompositorVsyncDispatcher`` notifies a
     26   ``CompositorWidgetVsyncObserver`` when remote compositing, or a
     27   ``CompositorVsyncScheduler::Observer`` when compositing in-process.
     28 5. If remote compositing, a vsync notification is sent from the
     29   ``CompositorWidgetVsyncObserver`` to the ``VsyncBridgeChild`` on the
     30   UI process, which sends an IPDL message to the ``VsyncBridgeParent``
     31   on the compositor thread of the GPU process, which then dispatches to
     32   ``CompositorVsyncScheduler::Observer``.
     33 6. The ``VsyncDispatcher`` notifies the Chrome
     34   ``RefreshTimer`` that a vsync has occurred.
     35 7. The ``VsyncDispatcher`` sends IPC messages to all content
     36   processes to tick their respective active ``RefreshTimer``.
     37 8. The ``Compositor`` dispatches input events on the *Compositor
     38   Thread*, then composites. Input events are only dispatched on the
     39   *Compositor Thread* on b2g.
     40 9. The ``RefreshDriver`` paints on the *Main Thread*.
     41 
     42 Hardware Vsync
     43 --------------
     44 
     45 Hardware vsync events from (1), occur on a specific ``Display`` Object.
     46 The ``Display`` object is responsible for enabling / disabling vsync on
     47 a per connected display basis. For example, if two monitors are
     48 connected, two ``Display`` objects will be created, each listening to
     49 vsync events for their respective displays. We require one ``Display``
     50 object per monitor as each monitor may have different vsync rates. As a
     51 fallback solution, we have one global ``Display`` object that can
     52 synchronize across all connected displays. The global ``Display`` is
     53 useful if a window is positioned halfway between the two monitors. Each
     54 platform will have to implement a specific ``Display`` object to hook
     55 and listen to vsync events. As of this writing, both Firefox OS and OS X
     56 create their own hardware specific *Hardware Vsync Thread* that executes
     57 after a vsync has occurred. OS X creates one *Hardware Vsync Thread* per
     58 ``CVDisplayLinkRef``. We do not currently support multiple displays, so
     59 we use one global ``CVDisplayLinkRef`` that works across all active
     60 displays. On Windows, we have to create a new platform ``thread`` that
     61 waits for DwmFlush(), which works across all active displays. Once the
     62 thread wakes up from DwmFlush(), the actual vsync timestamp is retrieved
     63 from DwmGetCompositionTimingInfo(), which is the timestamp that is
     64 actually passed into the compositor and refresh driver.
     65 
     66 When a vsync occurs on a ``Display``, the *Hardware Vsync Thread*
     67 callback fetches all ``CompositorVsyncDispatchers`` associated with the
     68 ``Display``. Each ``CompositorVsyncDispatcher`` is notified that a vsync
     69 has occurred with the vsync’s timestamp. It is the responsibility of the
     70 ``CompositorVsyncDispatcher`` to notify the ``Compositor`` that is
     71 awaiting vsync notifications. The ``Display`` will then notify the
     72 associated ``VsyncDispatcher``, which should notify all
     73 active ``RefreshDrivers`` to tick.
     74 
     75 All ``Display`` objects are encapsulated in a ``VsyncSource`` object.
     76 The ``VsyncSource`` object lives in ``gfxPlatform`` and is instantiated
     77 only on the parent process when ``gfxPlatform`` is created. The
     78 ``VsyncSource`` is destroyed when ``gfxPlatform`` is destroyed. It can
     79 also be destroyed when the layout frame rate pref (or other prefs that
     80 influence frame rate) are changed. This may mean we switch from hardware
     81 to software vsync (or vice versa) at runtime. During the switch, there
     82 may briefly be 2 vsync sources. Otherwise, there is only one
     83 ``VsyncSource`` object throughout the entire lifetime of Firefox. Each
     84 platform is expected to implement their own ``VsyncSource`` to manage
     85 vsync events. On OS X, this is through ``CVDisplayLinkRef``. On
     86 Windows, it should be through ``DwmGetCompositionTimingInfo``.
     87 
     88 Compositor
     89 ----------
     90 
     91 When the ``CompositorVsyncDispatcher`` is notified of the vsync event,
     92 the ``CompositorVsyncScheduler::Observer`` associated with the
     93 ``CompositorVsyncDispatcher`` begins execution. Since the
     94 ``CompositorVsyncDispatcher`` executes on the *Hardware Vsync Thread*
     95 and the ``Compositor`` composites on the ``CompositorThread``, the
     96 ``CompositorVsyncScheduler::Observer`` posts a task to the
     97 ``CompositorThread``. The ``CompositorBridgeParent`` then composites.
     98 The model where the ``CompositorVsyncDispatcher`` notifies components on
     99 the *Hardware Vsync Thread*, and the component schedules the task on the
    100 appropriate thread is used everywhere.
    101 
    102 The ``CompositorVsyncScheduler::Observer`` listens to vsync events as
    103 needed and stops listening to vsync when composites are no longer
    104 scheduled or required. Every ``CompositorBridgeParent`` is associated
    105 and tied to one ``CompositorVsyncScheduler::Observer``, which is
    106 associated with the ``CompositorVsyncDispatcher``. Each
    107 ``CompositorBridgeParent`` is associated with one widget and is created
    108 when a new platform window or ``nsIWidget`` is created. The
    109 ``CompositorBridgeParent``, ``CompositorVsyncDispatcher``,
    110 ``CompositorVsyncScheduler::Observer``, and ``nsIWidget`` all have
    111 the same lifetimes, which are created and destroyed together.
    112 
    113 Out-of-process Compositors
    114 --------------------------
    115 
    116 When compositing out-of-process, this model changes slightly. In this
    117 case there are effectively two observers: a UI process observer
    118 (``CompositorWidgetVsyncObserver``), and the
    119 ``CompositorVsyncScheduler::Observer`` in the GPU process. There are
    120 also two dispatchers: the widget dispatcher in the UI process
    121 (``CompositorVsyncDispatcher``), and the IPDL-based dispatcher in the
    122 GPU process (``CompositorBridgeParent::NotifyVsync``). The UI process
    123 observer and the GPU process dispatcher are linked via an IPDL protocol
    124 called PVsyncBridge. ``PVsyncBridge`` is a top-level protocol for
    125 sending vsync notifications to the compositor thread in the GPU process.
    126 The compositor controls vsync observation through a separate actor,
    127 ``PCompositorWidget``, which (as a subactor for
    128 ``CompositorBridgeChild``) links the compositor thread in the GPU
    129 process to the main thread in the UI process.
    130 
    131 Out-of-process compositors do not go through
    132 ``CompositorVsyncDispatcher`` directly. Instead, the
    133 ``CompositorWidgetDelegate`` in the UI process creates one, and gives it
    134 a ``CompositorWidgetVsyncObserver``. This observer forwards
    135 notifications to a Vsync I/O thread, where ``VsyncBridgeChild`` then
    136 forwards the notification again to the compositor thread in the GPU
    137 process. The notification is received by a ``VsyncBridgeParent``. The
    138 GPU process uses the layers ID in the notification to find the correct
    139 compositor to dispatch the notification to.
    140 
    141 CompositorVsyncDispatcher
    142 -------------------------
    143 
    144 The ``CompositorVsyncDispatcher`` executes on the *Hardware Vsync
    145 Thread*. It contains references to the ``nsIWidget`` it is associated
    146 with and has a lifetime equal to the ``nsIWidget``. The
    147 ``CompositorVsyncDispatcher`` is responsible for notifying the
    148 ``CompositorBridgeParent`` that a vsync event has occurred. There can be
    149 multiple ``CompositorVsyncDispatchers`` per ``Display``, one
    150 ``CompositorVsyncDispatcher`` per window. The only responsibility of the
    151 ``CompositorVsyncDispatcher`` is to notify components when a vsync event
    152 has occurred, and to stop listening to vsync when no components require
    153 vsync events. We require one ``CompositorVsyncDispatcher`` per window so
    154 that we can handle multiple ``Displays``. When compositing in-process,
    155 the ``CompositorVsyncDispatcher`` is attached to the CompositorWidget
    156 for the window. When out-of-process, it is attached to the
    157 CompositorWidgetDelegate, which forwards observer notifications over
    158 IPDL. In the latter case, its lifetime is tied to a CompositorSession
    159 rather than the nsIWidget.
    160 
    161 Multiple Displays
    162 -----------------
    163 
    164 The ``VsyncSource`` has an API to switch a ``CompositorVsyncDispatcher``
    165 from one ``Display`` to another ``Display``. For example, when one
    166 window either goes into full screen mode or moves from one connected
    167 monitor to another. When one window moves to another monitor, we expect
    168 a platform specific notification to occur. The detection of when a
    169 window enters full screen mode or moves is not covered by Silk itself,
    170 but the framework is built to support this use case. The expected flow
    171 is that the OS notification occurs on ``nsIWidget``, which retrieves the
    172 associated ``CompositorVsyncDispatcher``. The
    173 ``CompositorVsyncDispatcher`` then notifies the ``VsyncSource`` to
    174 switch to the correct ``Display`` the ``CompositorVsyncDispatcher`` is
    175 connected to. Because the notification works through the ``nsIWidget``,
    176 the actual switching of the ``CompositorVsyncDispatcher`` to the correct
    177 ``Display`` should occur on the *Main Thread*. The current
    178 implementation of Silk does not handle this case and needs to be built
    179 out.
    180 
    181 CompositorVsyncScheduler::Observer
    182 ----------------------------------
    183 
    184 The ``CompositorVsyncScheduler::Observer`` handles the vsync
    185 notifications and interactions with the ``CompositorVsyncDispatcher``.
    186 When the ``Compositor`` requires a scheduled composite, it notifies the
    187 ``CompositorVsyncScheduler::Observer`` that it needs to listen to vsync.
    188 The ``CompositorVsyncScheduler::Observer`` then observes / unobserves
    189 vsync as needed from the ``CompositorVsyncDispatcher`` to enable
    190 composites.
    191 
    192 GeckoTouchDispatcher
    193 --------------------
    194 
    195 The ``GeckoTouchDispatcher`` is a singleton that resamples touch events
    196 to smooth out jank while tracking a user’s finger. Because input and
    197 composite are linked together, the
    198 ``CompositorVsyncScheduler::Observer`` has a reference to the
    199 ``GeckoTouchDispatcher`` and vice versa.
    200 
    201 Input Events
    202 ------------
    203 
    204 One large goal of Silk is to align touch events with vsync events. On
    205 Firefox OS, touchscreens often have different touch scan rates than the
    206 display refreshes. A Flame device has a touch refresh rate of 75 HZ,
    207 while a Nexus 4 has a touch refresh rate of 100 HZ, while the device’s
    208 display refresh rate is 60HZ. When a vsync event occurs, we resample
    209 touch events, and then dispatch the resampled touch event to APZ. Touch
    210 events on Firefox OS occur on a *Touch Input Thread* whereas they are
    211 processed by APZ on the *APZ Controller Thread*. We use `Google
    212 Android’s touch
    213 resampling <https://web.archive.org/web/20200909082458/http://www.masonchang.com/blog/2014/8/25/androids-touch-resampling-algorithm>`__
    214 algorithm to resample touch events.
    215 
    216 Currently, we have a strict ordering between Composites and touch
    217 events. When a touch event occurs on the *Touch Input Thread*, we store
    218 the touch event in a queue. When a vsync event occurs, the
    219 ``CompositorVsyncDispatcher`` notifies the ``Compositor`` of a vsync
    220 event, which notifies the ``GeckoTouchDispatcher``. The
    221 ``GeckoTouchDispatcher`` processes the touch event first on the *APZ
    222 Controller Thread*, which is the same as the *Compositor Thread* on b2g,
    223 then the ``Compositor`` finishes compositing. We require this strict
    224 ordering because if a vsync notification is dispatched to both the
    225 ``Compositor`` and ``GeckoTouchDispatcher`` at the same time, a race
    226 condition occurs between processing the touch event and therefore
    227 position versus compositing. In practice, this creates very janky
    228 scrolling. As of this writing, we have not analyzed input events on
    229 desktop platforms.
    230 
    231 One slight quirk is that input events can start a composite, for example
    232 during a scroll and after the ``Compositor`` is no longer listening to
    233 vsync events. In these cases, we notify the ``Compositor`` to observe
    234 vsync so that it dispatches touch events. If touch events were not
    235 dispatched, and since the ``Compositor`` is not listening to vsync
    236 events, the touch events would never be dispatched. The
    237 ``GeckoTouchDispatcher`` handles this case by always forcing the
    238 ``Compositor`` to listen to vsync events while touch events are
    239 occurring.
    240 
    241 Widget, Compositor, CompositorVsyncDispatcher, GeckoTouchDispatcher Shutdown Procedure
    242 --------------------------------------------------------------------------------------
    243 
    244 When the `nsIWidget shuts
    245 down <https://hg.mozilla.org/mozilla-central/file/0df249a0e4d3/widget/nsIWidget.cpp#l182>`__
    246 - It calls nsIWidget::DestroyCompositor on the *Gecko Main Thread*.
    247 During nsIWidget::DestroyCompositor, it first destroys the
    248 CompositorBridgeChild. CompositorBridgeChild sends a sync IPC call to
    249 CompositorBridgeParent::RecvStop, which calls
    250 `CompositorBridgeParent::Destroy <https://hg.mozilla.org/mozilla-central/file/ab0490972e1e/gfx/layers/ipc/CompositorParent.cpp#l509>`__.
    251 During this time, the *main thread* is blocked on the parent process.
    252 CompositorBridgeParent::RecvStop runs on the *Compositor thread* and
    253 cleans up some resources, including setting the
    254 ``CompositorVsyncScheduler::Observer`` to nullptr.
    255 CompositorBridgeParent::RecvStop also explicitly keeps the
    256 CompositorBridgeParent alive and posts another task to run
    257 CompositorBridgeParent::DeferredDestroy on the Compositor loop so that
    258 all ipdl code can finish executing. The
    259 ``CompositorVsyncScheduler::Observer`` also unobserves from vsync and
    260 cancels any pending composite tasks. Once
    261 CompositorBridgeParent::RecvStop finishes, the *main thread* in the
    262 parent process continues shutting down the nsIWidget.
    263 
    264 At the same time, the *Compositor thread* is executing tasks until
    265 CompositorBridgeParent::DeferredDestroy runs, which flushes the
    266 compositor message loop. Now we have two tasks as both the nsIWidget
    267 releases a reference to the Compositor on the *main thread* during
    268 destruction and the CompositorBridgeParent::DeferredDestroy releases a
    269 reference to the CompositorBridgeParent on the *Compositor Thread*.
    270 Finally, the CompositorBridgeParent itself is destroyed on the *main
    271 thread* once both references are gone due to explicit `main thread
    272 destruction <https://hg.mozilla.org/mozilla-central/file/50b95032152c/gfx/layers/ipc/CompositorParent.h#l148>`__.
    273 
    274 With the ``CompositorVsyncScheduler::Observer``, any accesses to the
    275 widget after nsIWidget::DestroyCompositor executes are invalid. Any
    276 accesses to the compositor between the time the
    277 nsIWidget::DestroyCompositor runs and the
    278 CompositorVsyncScheduler::Observer’s destructor runs aren’t safe yet a
    279 hardware vsync event could occur between these times. Since any tasks
    280 posted on the Compositor loop after
    281 CompositorBridgeParent::DeferredDestroy is posted are invalid, we make
    282 sure that no vsync tasks can be posted once
    283 CompositorBridgeParent::RecvStop executes and DeferredDestroy is posted
    284 on the Compositor thread. When the sync call to
    285 CompositorBridgeParent::RecvStop executes, we explicitly set the
    286 CompositorVsyncScheduler::Observer to null to prevent vsync
    287 notifications from occurring. If vsync notifications were allowed to
    288 occur, since the ``CompositorVsyncScheduler::Observer``\ ’s vsync
    289 notification executes on the *hardware vsync thread*, it would post a
    290 task to the Compositor loop and may execute after
    291 CompositorBridgeParent::DeferredDestroy. Thus, we explicitly shut down
    292 vsync events in the ``CompositorVsyncDispatcher`` and
    293 ``CompositorVsyncScheduler::Observer`` during nsIWidget::Shutdown to
    294 prevent any vsync tasks from executing after
    295 CompositorBridgeParent::DeferredDestroy.
    296 
    297 The ``CompositorVsyncDispatcher`` may be destroyed on either the *main
    298 thread* or *Compositor Thread*, since both the nsIWidget and
    299 ``CompositorVsyncScheduler::Observer`` race to destroy on different
    300 threads. nsIWidget is destroyed on the *main thread* and releases a
    301 reference to the ``CompositorVsyncDispatcher`` during destruction. The
    302 ``CompositorVsyncScheduler::Observer`` has a race to be destroyed either
    303 during CompositorBridgeParent shutdown or from the
    304 ``GeckoTouchDispatcher`` which is destroyed on the main thread with
    305 `ClearOnShutdown <https://hg.mozilla.org/mozilla-central/file/21567e9a6e40/xpcom/base/ClearOnShutdown.h#l15>`__.
    306 Whichever object, the CompositorBridgeParent or the
    307 ``GeckoTouchDispatcher`` is destroyed last will hold the last reference
    308 to the ``CompositorVsyncDispatcher``, which destroys the object.
    309 
    310 Refresh Driver
    311 --------------
    312 
    313 The Refresh Driver is ticked from a `single active
    314 timer <https://hg.mozilla.org/mozilla-central/file/ab0490972e1e/layout/base/nsRefreshDriver.cpp#l11>`__.
    315 The assumption is that there are multiple ``RefreshDrivers`` connected
    316 to a single ``RefreshTimer``. There are two ``RefreshTimers``: an active
    317 and an inactive ``RefreshTimer``. Each Tab has its own
    318 ``RefreshDriver``, which connects to one of the global
    319 ``RefreshTimers``. The ``RefreshTimers`` execute on the *Main Thread*
    320 and tick their connected ``RefreshDrivers``. We do not want to break
    321 this model of multiple ``RefreshDrivers`` per a set of two global
    322 ``RefreshTimers``. Each ``RefreshDriver`` switches between the active
    323 and inactive ``RefreshTimer``.
    324 
    325 Instead, we create a new ``RefreshTimer``, the ``VsyncRefreshTimer``
    326 which ticks based on vsync messages. We replace the current active timer
    327 with a ``VsyncRefreshTimer``. All tabs will then tick based on this new
    328 active timer. Since the ``RefreshTimer`` has a lifetime of the process,
    329 we only need to create a single ``VsyncDispatcher`` per
    330 ``Display`` when Firefox starts. Even if we do not have any content
    331 processes, the Chrome process will still need a ``VsyncRefreshTimer``,
    332 thus we can associate the ``VsyncDispatcher`` with each
    333 ``Display``.
    334 
    335 When Firefox starts, we initially create a new ``VsyncRefreshTimer`` in
    336 the Chrome process. The ``VsyncRefreshTimer`` will listen to vsync
    337 notifications from ``VsyncDispatcher`` on the global
    338 ``Display``. When nsRefreshDriver::Shutdown executes, it will delete the
    339 ``VsyncRefreshTimer``. This creates a problem as all the
    340 ``RefreshTimers`` are currently manually memory managed whereas
    341 ``VsyncObservers`` are ref counted. To work around this problem, we
    342 create a new ``RefreshDriverVsyncObserver`` as an inner class to
    343 ``VsyncRefreshTimer``, which actually receives vsync notifications. It
    344 then ticks the ``RefreshDrivers`` inside ``VsyncRefreshTimer``.
    345 
    346 With Content processes, the start up process is more complicated. We
    347 send vsync IPC messages via the use of the PBackground thread on the
    348 parent process, which allows us to send messages from the Parent
    349 process’ without waiting on the *main thread*. This sends messages from
    350 the Parent::\ *PBackground Thread* to the Child::\ *Main Thread*. The
    351 *main thread* receiving IPC messages on the content process is
    352 acceptable because ``RefreshDrivers`` must execute on the *main thread*.
    353 However, there is some amount of time required to setup the IPC
    354 connection upon process creation and during this time, the
    355 ``RefreshDrivers`` must tick to set up the process. To get around this,
    356 we initially use software ``RefreshTimers`` that already exist during
    357 content process startup and swap in the ``VsyncRefreshTimer`` once the
    358 IPC connection is created.
    359 
    360 During nsRefreshDriver::ChooseTimer, we create an async PBackground IPC
    361 open request to create a ``VsyncParent`` and ``VsyncChild``. At the same
    362 time, we create a software ``RefreshTimer`` and tick the
    363 ``RefreshDrivers`` as normal. Once the PBackground callback is executed
    364 and an IPC connection exists, we swap all ``RefreshDrivers`` currently
    365 associated with the active ``RefreshTimer`` and swap the
    366 ``RefreshDrivers`` to use the ``VsyncRefreshTimer``. Since all
    367 interactions on the content process occur on the main thread, there are
    368 no need for locks. The ``VsyncParent`` listens to vsync events through
    369 the ``VsyncRefreshTimerDispatcher`` on the parent side and sends vsync
    370 IPC messages to the ``VsyncChild``. The ``VsyncChild`` notifies the
    371 ``VsyncRefreshTimer`` on the content process.
    372 
    373 During the shutdown process of the content process, ActorDestroy is
    374 called on the ``VsyncChild`` and ``VsyncParent`` due to the normal
    375 PBackground shutdown process. Once ActorDestroy is called, no IPC
    376 messages should be sent across the channel. After ActorDestroy is
    377 called, the IPDL machinery will delete the **VsyncParent/Child** pair.
    378 The ``VsyncParent``, due to being a ``VsyncObserver``, is ref counted.
    379 After ``VsyncParent::ActorDestroy`` is called, it unregisters itself
    380 from the ``VsyncDispatcher``, which holds the last reference
    381 to the ``VsyncParent``, and the object will be deleted.
    382 
    383 Thus the overall flow during normal execution is:
    384 
    385 1. VsyncSource::Display::VsyncDispatcher receives a Vsync
    386   notification from the OS in the parent process.
    387 2. VsyncDispatcher notifies
    388   VsyncRefreshTimer::RefreshDriverVsyncObserver that a vsync occurred on
    389   the parent process on the hardware vsync thread.
    390 3. VsyncDispatcher notifies the VsyncParent on the hardware
    391   vsync thread that a vsync occurred.
    392 4. The VsyncRefreshTimer::RefreshDriverVsyncObserver in the parent
    393   process posts a task to the main thread that ticks the refresh
    394   drivers.
    395 5. VsyncParent posts a task to the PBackground thread to send a vsync
    396   IPC message to VsyncChild.
    397 6. VsyncChild receive a vsync notification on the content process on the
    398   main thread and ticks their respective RefreshDrivers.
    399 
    400 Compressing Vsync Messages
    401 --------------------------
    402 
    403 Vsync messages occur quite often and the *main thread* can be busy for
    404 long periods of time due to JavaScript. Consistently sending vsync
    405 messages to the refresh driver timer can flood the *main thread* with
    406 refresh driver ticks, causing even more delays. To avoid this problem,
    407 we compress vsync messages on both the parent and child processes.
    408 
    409 On the parent process, newer vsync messages update a vsync timestamp but
    410 do not actually queue any tasks on the *main thread*. Once the parent
    411 process’ *main thread* executes the refresh driver tick, it uses the
    412 most updated vsync timestamp to tick the refresh driver. After the
    413 refresh driver has ticked, one single vsync message is queued for
    414 another refresh driver tick task. On the content process, the IPDL
    415 ``compress`` keyword automatically compresses IPC messages.
    416 
    417 Multiple Monitors
    418 -----------------
    419 
    420 In order to have multiple monitor support for the ``RefreshDrivers``, we
    421 have multiple active ``RefreshTimers``. Each ``RefreshTimer`` is
    422 associated with a specific ``Display`` via an id and tick when it’s
    423 respective ``Display`` vsync occurs. We have **N RefreshTimers**, where
    424 N is the number of connected displays. Each ``RefreshTimer`` still has
    425 multiple ``RefreshDrivers``.
    426 
    427 When a tab or window changes monitors, the ``nsIWidget`` receives a
    428 display changed notification. Based on which display the window is on,
    429 the window switches to the correct ``VsyncDispatcher`` and
    430 ``CompositorVsyncDispatcher`` on the parent process based on the display
    431 id. Each ``TabParent`` should also send a notification to their child.
    432 Each ``TabChild``, given the display ID, switches to the correct
    433 ``RefreshTimer`` associated with the display ID. When each display vsync
    434 occurs, it sends one IPC message to notify vsync. The vsync message
    435 contains a display ID, to tick the appropriate ``RefreshTimer`` on the
    436 content process. There is still only one **VsyncParent/VsyncChild**
    437 pair, just each vsync notification will include a display ID, which maps
    438 to the correct ``RefreshTimer``.
    439 
    440 Object Lifetime
    441 ---------------
    442 
    443 1. CompositorVsyncDispatcher - Lives as long as the nsIWidget
    444   associated with the VsyncDispatcher
    445 2. CompositorVsyncScheduler::Observer - Lives and dies the same time as
    446   the CompositorBridgeParent.
    447 3. VsyncDispatcher - As long as the associated display
    448   object, which is the lifetime of Firefox.
    449 4. VsyncSource - Lives as long as the gfxPlatform on the chrome process,
    450   which is the lifetime of Firefox.
    451 5. VsyncParent/VsyncChild - Lives as long as the content process
    452 6. RefreshTimer - Lives as long as the process
    453 
    454 Threads
    455 -------
    456 
    457 All ``VsyncObservers`` are notified on the *Hardware Vsync Thread*. It
    458 is the responsibility of the ``VsyncObservers`` to post tasks to their
    459 respective correct thread. For example, the
    460 ``CompositorVsyncScheduler::Observer`` will be notified on the *Hardware
    461 Vsync Thread*, and post a task to the *Compositor Thread* to do the
    462 actual composition.
    463 
    464 1. Compositor Thread - Nothing changes
    465 2. Main Thread - PVsyncChild receives IPC messages on the main thread.
    466   We also enable/disable vsync on the main thread.
    467 3. PBackground Thread - Creates a connection from the PBackground thread
    468   on the parent process to the main thread in the content process.
    469 4. Hardware Vsync Thread - Every platform is different, but we always
    470   have the concept of a hardware vsync thread. Sometimes this is
    471   actually created by the host OS. On Windows, we have to create a
    472   separate platform thread that blocks on DwmFlush().