This directory contains the implementation of the CSS Paint API.
See CSS Paint API for the web exposed APIs this implements.
See Explainer of this feature, as well as Samples.
Historically the CSS Paint API (PaintWorklet) implementation ran on the main thread, but is currently being optimized to run on the compositor thread. We will use an example to show the workflow of both cases.
Here is a simple example of using PaintWorklet to draw something on the screen.
In our implementation, there is one PaintWorklet instance created from the frame. There are two PaintWorkletGlobalScope created to enforce stateless. The number of global scopes can be arbitrary, and our implementation chose two.
During PaintWorkletGlobalScope#registerPaint
, the Javascript inside the paint function is turned into a V8 paint callback. We randomly choose one of the two global scopes to execute the callback. The execution of the callback produces a PaintRecord, which contains a set of skia draw commands. The V8 paint callback is executed on a shared V8 isolate.
During the main thread paint, the PaintWorklet::Paint
is called, which executes the V8 paint callback synchronously. A PaintRecord is produced and passed to the compositor thread to raster.
During the main thread paint, a PaintWorkletInput is created and passed to the compositor. The PaintWorkletInput contains all necessary information for the compositor to run the V8 paint callback. The compositor thread asynchronously dispatches the V8 paint callback to a Worklet thread. The V8 paint callback is then executed on the Worklet thread and a PaintRecord is given back to the compositor thread. The compositor thread then rasters the PaintRecord.
Represents a class registered by the author through PaintWorkletGlobalScope#registerPaint
. Specifically this class holds onto the javascript constructor and paint functions of the class via persistent handles. This class keeps these functions alive so they don't get garbage collected.
The CSSPaintDefinition
also holds onto an instance of the paint class via a persistent handle. This instance is lazily created upon first use. If the constructor throws for some reason the constructor is marked as invalid and will always produce invalid images.
The PaintWorkletGlobalScope
has a map of paint name
to CSSPaintDefinition
.
CSSPaintImageGenerator
represents the interface from which the CSSPaintValue
can generate Image
s. This is done via the CSSPaintImageGenerator#paint
method. Each CSSPaintValue
owns a separate instance of CSSPaintImageGenerator
.
CSSPaintImageGeneratorImpl
is the implementation which lives in modules/csspaint
. (We have this interface / implementation split as core/
cannot depend on modules/
).
When created the generator will access its paint worklet and lookup it's corresponding CSSPaintDefinition
via PaintWorkletGlobalScope#findDefinition
.
If the paint worklet does not have a CSSPaintDefinition
matching the paint name
the CSSPaintImageGeneratorImpl
is placed in a “pending” map. Once a paint class with name
is registered the generator is notified so it can invalidate an display the correct image.
PaintGeneratedImage
is a Image
which just paints a single PaintRecord
.
A CSSPaintValue
can generate an image from the method CSSPaintImageGenerator#paint
. This method calls through to CSSPaintDefinition#paint
which actually invokes the javascript paint method. This method returns the PaintGeneratedImage
.
The CSSPaintDefinition
keeps a list of both native and custom properties it will invalidate on. During style invalidation ComputedStyle
checks if it has any CSSPaintValue
s, and if any of their properties have changed; if so it will invalidate paint for that ComputedStyle
.
If the CSSPaintValue
doesn‘t have a corresponding CSSPaintDefinition
yet, it doesn’t invalidate paint.