User:Roc/VsyncProposal

From MozillaWiki
Jump to: navigation, search

Basic Proposal For OMTC VSync

Use a single compositor thread, as today.

Provide a per-platform "RequestVSyncEvent" API. This is called by the compositor thread and ensures that at the next vsync, a notification event is dispatched to the compositor thread.

When the compositor thread starts processing a layer tree update message, if there is no vsync event pending, it calls RequestVSyncEvent. It does not composite at the end of the layer tree update.

When the compositor thread receives a vsync event, it takes the following steps:

  1. For each layer tree that has been updated since the last vsync event, it sends an async "refresh driver tick" message to the main thread that sent the layer tree update. The main thread processes a refresh driver tick message by ticking the refresh driver if there is work to do (and hence generating a new layer tree update). This replaces the use of timers to drive refresh driver animation.
  2. Immediately after the compositor thread has sent all refresh driver tick messages, it composites all windows that processed layer tree updates since the last vsync or that have active off-main-thread animations.
  3. If there are active off-main-thread animations, call RequestVSyncEvent.

Updates through ImageBridge or a putative WebGL worker would be treated as just another form of layer-tree update.

Analysis

This is pretty simple. It allows refresh driver ticks to use up to 100% of the main thread without dropping frames.

It should work OK with the current situation where a layer tree update can cause the main thread to block until the compositor thread has processed the update.

This approach adds one frame of latency over the minimum: instead of (usually) getting the result of a layer tree update onto the screen at the next vsync after the layer tree update, we have to wait until the vsync after that.

When dispatching a refresh driver tick message we know when we expect the frame to be displayed --- "2*the vsync interval from now" (plus platform-specific overhead). We can pass that information along to the main thread for synchronization purposes. Likewise we know the expected render time to use when sampling compositor-driven animations.

Possible Extensions

As-Fast-As-Possible Mode

For our "as fast as possible mode", we'd just make RequestVSyncEvent immediately post a vsync event back to the compositor thread.

Multiple Monitors

If we want to properly synchronize windows with the vsync signal for the monitor they're on, we would maintain a set of compositors per monitor and have RequestVSyncEvent take a monitor parameter. When processing a vsync event for a monitor, we would send refresh driver tick messages for all the updated windows for that monitor and then composite all changed windows for that monitor.

Lowering Latency

We can reduce latency for some workloads. Assume we designate a subset of the compositors as "low latency". Modify the vsync event handler as follows:

  1. Dispatch refresh driver ticks for low-latency windows.
  2. Wait for N ms, processing any layer-tree updates.
  3. Dispatch refresh driver ticks for all non-low-latency windows.
  4. Composite all updated windows.

N is a tunable parameter, possibly chosen dynamically, chosen so that we expect all window composites to easily complete in vsyncInterval-N ms.

Windows designated "low latency" whose refresh driver ticks complete in less than N ms will get minimum latency.

WebGL Workers

WebGL workers would fit into this scheme easily. The internal interface between a WebGL worker and the compositor thread would look very much like the interface between the main thread and the compositor thread; instead of a "refresh driver tick", the message from the compositor thread to the WebGL worker would wake up the worker to draw a new frame.

Handling Slow Painting

With the basic proposal, if a refresh driver tick takes a little more than a full vsync interval to complete, the frequency of refresh driver ticks basically drops to half the vsync rate.

If we instead want to fire refresh driver ticks as fast as possible in such a situation, that's easy. When a vsync event has been received between dispatching a refresh driver tick and receiving the layer-tree update, we dispatch the refresh driver tick immediately upon receiving the layer tree update instead of dispatching it at the next vsync event.