Compositor Property Trees
Property tree overview
Transforms and clips and effects, oh my!
Before:
root
opacity
draw
scroll parent clip
scroll parent
scroll child
transform
clip
draw
draw
scrolls by
clips by
fixed
position
positioned by
Layer Tree
Multiple hierarchies with property trees
transform
Property trees + Layer List
root
opacity
draw
scroll parent clip
scroll parent
scroll child
transform
clip
draw
draw
scrolls by
clips by
fixed
position
positioned by
Layer Tree
clip
viewport
scroll parent clip
draw
draw
draw
scroll parent
scroll child
scroll parent
opacity
Transform Tree
Clip Tree
Effect Tree
Scroll Tree
fixed position
scroll parent
Property trees
Trees are sparse -- not every layer has an interesting transform, clip, effect, or scroll
But cross-tree dependencies do exist
Layer list still has non-drawing layers (for now)
Property trees + Layer List
transform
clip
viewport
scroll parent clip
draw
draw
draw
scroll parent
scroll child
scroll parent
opacity
Transform Tree
Clip Tree
Effect Tree
Scroll Tree
fixed position
Transform tree
animated
transform
page
scale
Transform Tree
viewport
scroller
significant
transform
scroller
significant
transform
lowest common ancestor
A transform is significant if it isn’t a 2d translation
Render surfaces
Buffers that hold the output of drawing operations
Some operations require their inputs to first be drawn to an intermediate buffer:
Inputs
Opacity 0.5, no render surface
Opacity 0.5, with render surface
Effect tree
animated
opacity
non-1 opacity
Effect Tree
root render surface
mask
filter
Clip tree
clip
viewport
clip
Clip Tree
clip expander
clip
Scroll tree
non-fast scrollable region
main-thread scrolling reason
scroller
Scroll Tree
scroller
scroller
Layers
Each layer points to a node from each of the four trees
Each layer also has
Compositor driven effects
Updating property trees
Property trees are only built/re-built on the main thread
Scrolling
Given an input point in screen space:
Animation
Every compositor animation is associated with a property tree node
Each time an animation ticks:
Property tree outputs
Property tree outputs: draw properties
Layer draw properties
Render surface draw properties
Screen-space transform and draw transform
Screen-space transform: maps from local space to screen space
Draw transform (target-space transform): maps from local space to space of target render surface
Transform Tree
Layer
Render surface
screen-space transform
draw transform
Visible layer rects
The part of a layer that’s visible, taking into account all clipping, expressed in layer space
Used to decide:
Overestimating hurts performance but not correctness
Clip Tree
Layer
combine all ancestor clips
layer bounds
combined clip
visible layer rect
clip_rect and is_clipped
is_clipped: whether a clip needs to be applied at draw time
clip_rect: the clip to apply, expressed in target space
Unlike visible layer rect, this needs to be computed exactly for correctness
Clip Tree
Layer
combine clips not handled by target
clip_rect
Render surface
Drawable content rect
Size of layer in target space, intersected with clip_rect if is_clipped
Used to compute target surface’s content rect
layer bounds
map to target space
layer bounds in target space
clip_rect
drawable content rect
Implementation details
Where’s the code?
Files in cc/trees:
Property tree implementation
template <typename T>
class PropertyTree {
public:
int Insert(const T& tree_node, int parent_id);
T* Node(int id);
T* parent(const T* t);
private:
std::vector<T> nodes_;
};
Property tree node implementation
Conceptually, each node type is:
template <typename ValueType>
struct PropertyTreeNode {
int id;
int parent_id;
int owning_layer_id; // stable across property tree rebuilds
ValueType value;
};
But nodes are much larger in reality...
Transform node implementation
Additional fields:
gfx::Transform pre_local
gfx::Transform local
gfx::Transform post_local
gfx::Transform to_parent
int sticky_position_constraint_id
int source_node_id
int sorting_context_id
bool needs_local_transform_update
bool node_and_ancestors_are_animated_or_invertible
bool is_invertible
bool ancestors_are_invertible
bool has_potential_animation
bool is_currently_animating
bool to_screen_is_potentially_animated
bool has_only_translation_animations
bool flattens_inherited_transform
bool node_and_ancestors_are_flat
bool scrolls
bool should_be_snapped
bool moved_by_{inner, outer}_viewport_bounds_delta_{x,y}
bool in_subtree_of_page_scale_layer
bool transform_changed
float post_local_scale_factor
gfx::ScrollOffset scroll_offset
gfx::Vector2dF snap_amount
gfx::Vector2dF source_offset
gfx::Vector2dF source_to_parent
Inputs
Cached data
Avoiding tree walks when computing transforms
ToScreen and FromScreen are computed and cached for every node
Then, to compute the transform from node i to node j:�
FromScreen(j) * ToScreen(i)
...unless flattening gets in the way
Flattening
Flattening is a non-linear operation:
(flatten(A))-1 ≠ flatten(A-1)
Flattening between two nodes breaks the ToScreen/FromScreen trick
Need to tree walk... but the result can be cached
a | b | c | d |
e | f | g | h |
i | j | k | l |
m | n | o | p |
a | b | 0 | d |
e | f | 0 | h |
0 | 0 | 1 | 0 |
m | n | 0 | p |
Transform caching
Multiple layers with the same target can point to the same transform node
The first time we compute a draw transform involving a particular pair of nodes, the result gets cached
Transform Tree
Layer
Render surface
draw transform
Layer
Layer
Fixed position
Old layer-tree-based logic: undo scroll deltas in between a fixed-position layer and its container
New logic: rely on transform tree topology
Catch: Blink still positions fixed-position layers wrt their layer tree parent, undoing ancestor scrolling
scroller
Transform Tree
viewport
scroller
fixed position
transform
root
scroller
scroller
fixed
position
Layer Tree
source node
parent node
Effect node implementation
Additional fields:
float opacity
float screen_space_opacity
FilterOperations filters
FilterOperations background_filters
gfx::PointF filters_origin
SkBlendMode blend_mode
gfx::Vector2dF surface_contents_scale
gfx::Size unscaled_mask_target_size
bool has_render_surface
RenderSurfaceImpl* render_surface
bool surface_is_clipped
bool has_copy_request
bool hidden_by_backface_visibility
bool double_sided
bool is_drawn
bool subtree_hidden
bool has_potential_{filter, opacity}_animation
bool is_currently_animating_{filter, opacity}
bool effect_changed
int num_copy_requests_in_subtree
bool has_unclipped_descendants
int transform_id
int clip_id
int target_id
int mask_layer_id
Inputs
Cached data
Clip node implementation
Additional fields:
ClipType clip_type
gfx::RectF clip
std::unique_ptr<ClipExpander> clip_expander
gfx::RectF combined_clip_in_target_space
gfx::RectF clip_in_target_space
int transform_id
int target_transform_id
int target_effect_id
bool layer_clipping_uses_only_local_clip
bool layers_are_clipped
bool layers_are_clipped_when_surfaces_disabled
bool resets_clip
Inputs
Cached values -- will be removed when caching is moved
Inputs -- will be removed when caching is moved
Clip caching
Current approach: caching at each clip node before computing each layer’s draw properties
New approach: combine clips on-demand, cache results in separate structure
Scroll node implementation
Additional fields (all inputs):
bool scrollable
uint32_t main_thread_scrolling_reasons
bool contains_non_fast_scrollable_region
gfx::Size scroll_clip_layer_bounds
gfx::Size bounds
bool max_scroll_offset_affected_by_page_scale
bool is_{inner, outer}_viewport_scroll_layer
gfx::Vector2dF offset_to_transform_parent
bool should_flatten
bool user_scrollable_horizontal
bool user_scrollable_vertical
Building property trees
Now:
In SPv2, for renderers:
ui will continue using cc::PropertyTreeBuilder for now
Unit tests need cc::PropertyTreeBuilder too...
Unit tests
cc has lots of unit tests that construct layer trees!
Tests that construct trees of Layer (e.g. LayerTreeTests):
Tests that construct trees of LayerImpl:
Android WebView
Applies changes on the compositor thread
Easy to handle:
Trickier: Resourceless software mode
Roadmap
Completed work
Performance
| M48 (CDP) | M53 (Property trees + layer lists) | |
Android - 50th percentile | 0.19 ms | 0.16 ms | -15.8% |
Android - 99th percentile | 2 ms | 1.55 ms | -22.5% |
Mac - 50th percentile | 0.04 ms | 0.034 ms | -15% |
Mac - 99th percentile | 0.4 ms | 0.34 ms | -15% |
Windows - 50th percentile | 0.033 ms | 0.027 ms | -18.2% |
Windows - 99th percentile | 1 ms | 0.97 ms | -3% |
Draw properties computation time
Current and future work
Questions?