1 of 19

Off Main Thread CSS Paint

BlinkOn 10 (Toronto, Canada)

xidachen@chromium.org, smcgruer@chromium.org

2 of 19

Before CSS Paint

.box {� background-image: url(“cat.jpg”);�}

3 of 19

CSS Paint enables new possibilities

<style>.box {� background-image: paint(foo);�}�</style>�...�<script>�CSS.paintWorklet.addModule('foo.js');�</script>

class FooPainter {� paint(ctx, geometry, properties) {� // …� }�}��registerPaint('foo', FooPainter);

4 of 19

CSS Paint enables new possibilities

5 of 19

More interesting with animations

<style>�.box {� --size: 100;� background-image: paint(foo);�}�</style>�...

<script>�CSS.paintWorklet.addModule('foo.js');��myBox.animate({� [ --size: [ 100, 200 ] ],�}, {� duration: 2500,�});�</script>

Added to static getter function in FooPainter.

6 of 19

More interesting with animations

7 of 19

Behind the scene...

Main thread is very busy doing:

  • Style
  • Layout
  • Paint
  • Animation

for all the 60 frames of that animation.�

8 of 19

Jank-free vs janky main thread

  • On a jank-free main thread

  • On a janky main thread

9 of 19

Wouldn't it be great if we could...

  • Not add more work to the main thread that is already busy?
  • Make the paint worklet animation unaffected by a janky main thread?

10 of 19

Solution: move it off the main thread

High-level idea:

  • Blink paint creates a placeholder for paint worklet.
  • Commit gives this placeholder to the compositor thread.
  • The compositor thread:
    • Runs any necessary animations
    • Dispatches the paint callback to a worklet thread and receives the result.
    • Fills in the placeholder with the result.

11 of 19

A bit more technical detail

Now

Paint

Tiling,

Raster, etc.

Skia

draw

cmds

Commit

Blink (Main Thread)

Compositor (Impl Thread)

PaintWorklet context (v8)

Animations

SLOW!

12 of 19

A bit more technical detail

Future

Paint

Composited Animations

Commit

Blink (Main Thread)

Compositor (Impl Thread)

Create PaintWorkletInputs

Tiling,

Raster, etc.

PaintWorklet Thread

LayerTree

HostImpl

FAST!

PaintWorklet context (v8)

PWIs

Skia

draw cmds

Impl-side Invalidation

13 of 19

Deep Dive - PaintWorkletInput

  • Encompasses all that is needed to find + execute a PaintWorklet.
  • Used across many threads - thread-safe refcounted.
  • But contains thread-unsafe data!

PaintWorkletInput

"green"

Name

200x500

Size

{

--foo: 20,

left: 50px,

color: 'red',

}

StyleMap

14 of 19

Deep Dive - Compositor

PaintWorklet affecting

animations tick

Scheduler::Set

NeedsImplSide

Invalidation

LTHI::UpdateSync

TreeAfterCommit

OrImplSideInvalidation

Blink Commit

Dispatch paint to paint worklet thread

LTHI::HandlePaint

WorkletResults

LTHI::

PrepareTiles

Unblock activation

if no

dirty PWIs

Dirty PaintWorkletInputs

Results

15 of 19

Deep Dive - PaintWorklet Thread

PaintWorklet thread

  • Deduplication
  • Minimum thread hops

16 of 19

Current Status

  • Basic prototype in codebase behind a flag:
    • --enable-blink-features=OffMainThreadCSSPaint
    • Almost works e2e; one CL missing to fill in a few details.
  • Still to do:
    • cc-side re-architecture to these slides (current design uses raster)
    • Integration with cc-animations.
    • Fallback to main for PaintWorklets we can't (yet) support - <image> inputs.
    • Testing. Testing. Testing.

17 of 19

Summary

  • CSS Paint hands power to developers.
  • But it affects, and is affected by, main thread jank!
  • Our goal is to move CSS Paint off the main thread.
    • Then animations can be smooth even if the main thread is janky. And who doesn't love a smooth animation?

18 of 19

Questions?

19 of 19

Challenges

  • WTF::String
  • Cross-thread style map
  • DO NOT jank the compositor thread!