2 ==========================
4 .. image:: SilkArchitecture.png
9 Our current architecture is to align three components to hardware vsync
13 2. RefreshDriver / Painting
16 The flow of our rendering engine is as follows:
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 ``RefreshTimerVsyncDispatcher``.
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 ``RefreshTimerVsyncDispatcher`` notifies the Chrome
34 ``RefreshTimer`` that a vsync has occurred.
35 7. The ``RefreshTimerVsyncDispatcher`` 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*.
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.
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 ``RefreshTimerVsyncDispatcher``, which should notify all
73 active ``RefreshDrivers`` to tick.
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``.
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.
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 ``nsBaseWidget`` is created. The
109 ``CompositorBridgeParent``, ``CompositorVsyncDispatcher``,
110 ``CompositorVsyncScheduler::Observer``, and ``nsBaseWidget`` all have
111 the same lifetimes, which are created and destroyed together.
113 Out-of-process Compositors
114 --------------------------
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.
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.
141 CompositorVsyncDispatcher
142 -------------------------
144 The ``CompositorVsyncDispatcher`` executes on the *Hardware Vsync
145 Thread*. It contains references to the ``nsBaseWidget`` it is associated
146 with and has a lifetime equal to the ``nsBaseWidget``. 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.
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
181 CompositorVsyncScheduler::Observer
182 ----------------------------------
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
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.
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
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.
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
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
241 Widget, Compositor, CompositorVsyncDispatcher, GeckoTouchDispatcher Shutdown Procedure
242 --------------------------------------------------------------------------------------
244 When the `nsBaseWidget shuts
245 down <https://hg.mozilla.org/mozilla-central/file/0df249a0e4d3/widget/nsBaseWidget.cpp#l182>`__
246 - It calls nsBaseWidget::DestroyCompositor on the *Gecko Main Thread*.
247 During nsBaseWidget::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 nsBaseWidget.
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 nsBaseWidget
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>`__.
274 With the ``CompositorVsyncScheduler::Observer``, any accesses to the
275 widget after nsBaseWidget::DestroyCompositor executes are invalid. Any
276 accesses to the compositor between the time the
277 nsBaseWidget::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 nsBaseWidget::Shutdown to
294 prevent any vsync tasks from executing after
295 CompositorBridgeParent::DeferredDestroy.
297 The ``CompositorVsyncDispatcher`` may be destroyed on either the *main
298 thread* or *Compositor Thread*, since both the nsBaseWidget and
299 ``CompositorVsyncScheduler::Observer`` race to destroy on different
300 threads. nsBaseWidget 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.
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``.
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 ``RefreshTimerVsyncDispatcher`` 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 ``RefreshTimerVsyncDispatcher`` with each
335 When Firefox starts, we initially create a new ``VsyncRefreshTimer`` in
336 the Chrome process. The ``VsyncRefreshTimer`` will listen to vsync
337 notifications from ``RefreshTimerVsyncDispatcher`` 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``.
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.
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.
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 ``RefreshTimerVsyncDispatcher``, which holds the last reference
381 to the ``VsyncParent``, and the object will be deleted.
383 Thus the overall flow during normal execution is:
385 1. VsyncSource::Display::RefreshTimerVsyncDispatcher receives a Vsync
386 notification from the OS in the parent process.
387 2. RefreshTimerVsyncDispatcher notifies
388 VsyncRefreshTimer::RefreshDriverVsyncObserver that a vsync occurred on
389 the parent process on the hardware vsync thread.
390 3. RefreshTimerVsyncDispatcher 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
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.
400 Compressing Vsync Messages
401 --------------------------
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.
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.
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``.
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 ``RefreshTimerVsyncDispatcher`` 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``.
443 1. CompositorVsyncDispatcher - Lives as long as the nsBaseWidget
444 associated with the VsyncDispatcher
445 2. CompositorVsyncScheduler::Observer - Lives and dies the same time as
446 the CompositorBridgeParent.
447 3. RefreshTimerVsyncDispatcher - 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
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
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().