1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6
7 #include <algorithm>
8 #include <set>
9 #include <string>
10 #include <utility>
11 #include <vector>
12
13 #include "base/check_op.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/numerics/checked_math.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "base/trace_event/traced_value.h"
18 #include "cc/trees/clip_node.h"
19 #include "cc/trees/compositor_commit_data.h"
20 #include "cc/trees/effect_node.h"
21 #include "cc/trees/layer_tree_impl.h"
22 #include "cc/trees/property_tree.h"
23 #include "cc/trees/scroll_node.h"
24 #include "cc/trees/transform_node.h"
25 #include "components/viz/common/frame_sinks/copy_output_request.h"
26 #include "ui/gfx/geometry/vector2d_conversions.h"
27
28 namespace cc {
29
30 template <typename T>
PropertyTree()31 PropertyTree<T>::PropertyTree() : needs_update_(false) {
32 nodes_.push_back(T());
33 back()->id = kRootNodeId;
34 back()->parent_id = kInvalidNodeId;
35 }
36
37 // Equivalent to
38 // PropertyTree<T>::~PropertyTree() = default;
39 // but due to a gcc bug the generated destructor will have wrong symbol
40 // visibility in component build.
41 template <typename T>
42 PropertyTree<T>::~PropertyTree() = default;
43
44 template <typename T>
45 PropertyTree<T>& PropertyTree<T>::operator=(const PropertyTree<T>&) = default;
46
47 #define DCHECK_NODE_EXISTENCE(check_node_existence, state, property, \
48 needs_rebuild) \
49 DCHECK(!check_node_existence || ((!state.currently_running[property] && \
50 !state.potentially_animating[property]) || \
51 needs_rebuild))
52
TransformTree()53 TransformTree::TransformTree()
54 : page_scale_factor_(1.f),
55 device_scale_factor_(1.f),
56 device_transform_scale_factor_(1.f) {
57 cached_data_.push_back(TransformCachedNodeData());
58 }
59
60 TransformTree::~TransformTree() = default;
61 TransformTree& TransformTree::operator=(const TransformTree&) = default;
62
63 template <typename T>
Insert(const T & tree_node,int parent_id)64 int PropertyTree<T>::Insert(const T& tree_node, int parent_id) {
65 DCHECK_GT(nodes_.size(), 0u);
66 nodes_.push_back(tree_node);
67 T& node = nodes_.back();
68 node.parent_id = parent_id;
69 node.id = static_cast<int>(nodes_.size()) - 1;
70 return node.id;
71 }
72
73 template <typename T>
clear()74 void PropertyTree<T>::clear() {
75 needs_update_ = false;
76 nodes_.clear();
77 nodes_.push_back(T());
78 back()->id = kRootNodeId;
79 back()->parent_id = kInvalidNodeId;
80
81 #if DCHECK_IS_ON()
82 PropertyTree<T> tree;
83 DCHECK(tree == *this);
84 #endif
85 }
86
87 #if DCHECK_IS_ON()
88 template <typename T>
operator ==(const PropertyTree<T> & other) const89 bool PropertyTree<T>::operator==(const PropertyTree<T>& other) const {
90 return nodes_ == other.nodes() && needs_update_ == other.needs_update();
91 }
92 #endif
93
94 template <typename T>
AsValueInto(base::trace_event::TracedValue * value) const95 void PropertyTree<T>::AsValueInto(base::trace_event::TracedValue* value) const {
96 value->BeginArray("nodes");
97 for (const auto& node : nodes_) {
98 value->BeginDictionary();
99 node.AsValueInto(value);
100 value->EndDictionary();
101 }
102 value->EndArray();
103 }
104
105 template class PropertyTree<TransformNode>;
106 template class PropertyTree<ClipNode>;
107 template class PropertyTree<EffectNode>;
108 template class PropertyTree<ScrollNode>;
109
Insert(const TransformNode & tree_node,int parent_id)110 int TransformTree::Insert(const TransformNode& tree_node, int parent_id) {
111 int node_id = PropertyTree<TransformNode>::Insert(tree_node, parent_id);
112 DCHECK_EQ(node_id, static_cast<int>(cached_data_.size()));
113
114 cached_data_.push_back(TransformCachedNodeData());
115 return node_id;
116 }
117
clear()118 void TransformTree::clear() {
119 PropertyTree<TransformNode>::clear();
120
121 page_scale_factor_ = 1.f;
122 device_scale_factor_ = 1.f;
123 device_transform_scale_factor_ = 1.f;
124 nodes_affected_by_outer_viewport_bounds_delta_.clear();
125 cached_data_.clear();
126 cached_data_.push_back(TransformCachedNodeData());
127 sticky_position_data_.clear();
128
129 #if DCHECK_IS_ON()
130 DCHECK(TransformTree() == *this);
131 #endif
132 }
133
set_needs_update(bool needs_update)134 void TransformTree::set_needs_update(bool needs_update) {
135 if (needs_update && !PropertyTree<TransformNode>::needs_update())
136 property_trees()->UpdateTransformTreeUpdateNumber();
137 PropertyTree<TransformNode>::set_needs_update(needs_update);
138 }
139
FindNodeFromElementId(ElementId id)140 TransformNode* TransformTree::FindNodeFromElementId(ElementId id) {
141 auto iterator = property_trees()->element_id_to_transform_node_index.find(id);
142 if (iterator == property_trees()->element_id_to_transform_node_index.end())
143 return nullptr;
144
145 return Node(iterator->second);
146 }
147
OnTransformAnimated(ElementId element_id,const gfx::Transform & transform)148 bool TransformTree::OnTransformAnimated(ElementId element_id,
149 const gfx::Transform& transform) {
150 TransformNode* node = FindNodeFromElementId(element_id);
151 DCHECK(node);
152 if (node->local == transform)
153 return false;
154 node->local = transform;
155 node->needs_local_transform_update = true;
156 node->transform_changed = true;
157 property_trees()->changed = true;
158 set_needs_update(true);
159 return true;
160 }
161
ResetChangeTracking()162 void TransformTree::ResetChangeTracking() {
163 for (int id = TransformTree::kContentsRootNodeId;
164 id < static_cast<int>(size()); ++id) {
165 TransformNode* node = Node(id);
166 node->transform_changed = false;
167 }
168 }
169
UpdateTransforms(int id)170 void TransformTree::UpdateTransforms(int id) {
171 TransformNode* node = Node(id);
172 TransformNode* parent_node = parent(node);
173 DCHECK(parent_node);
174 // TODO(flackr): Only dirty when scroll offset changes.
175 if (node->sticky_position_constraint_id >= 0 ||
176 node->needs_local_transform_update) {
177 UpdateLocalTransform(node);
178 } else {
179 UndoSnapping(node);
180 }
181 UpdateScreenSpaceTransform(node, parent_node);
182 UpdateAnimationProperties(node, parent_node);
183 UpdateSnapping(node);
184 UpdateTransformChanged(node, parent_node);
185 UpdateNodeAndAncestorsAreAnimatedOrInvertible(node, parent_node);
186 UpdateNodeOrAncestorsWillChangeTransform(node, parent_node);
187
188 DCHECK(!node->needs_local_transform_update);
189 }
190
IsDescendant(int desc_id,int source_id) const191 bool TransformTree::IsDescendant(int desc_id, int source_id) const {
192 while (desc_id != source_id) {
193 if (desc_id == kInvalidNodeId)
194 return false;
195 desc_id = Node(desc_id)->parent_id;
196 }
197 return true;
198 }
199
CombineTransformsBetween(int source_id,int dest_id,gfx::Transform * transform) const200 void TransformTree::CombineTransformsBetween(int source_id,
201 int dest_id,
202 gfx::Transform* transform) const {
203 DCHECK(source_id > dest_id);
204 const TransformNode* current = Node(source_id);
205 const TransformNode* dest = Node(dest_id);
206 // Combine transforms to and from the screen when possible. Since flattening
207 // is a non-linear operation, we cannot use this approach when there is
208 // non-trivial flattening between the source and destination nodes. For
209 // example, consider the tree R->A->B->C, where B flattens its inherited
210 // transform, and A has a non-flat transform. Suppose C is the source and A is
211 // the destination. The expected result is C * B. But C's to_screen
212 // transform is C * B * flattened(A * R), and A's from_screen transform is
213 // R^{-1} * A^{-1}. If at least one of A and R isn't flat, the inverse of
214 // flattened(A * R) won't be R^{-1} * A{-1}, so multiplying C's to_screen and
215 // A's from_screen will not produce the correct result.
216 if (!dest ||
217 (dest->ancestors_are_invertible && dest->node_and_ancestors_are_flat)) {
218 transform->ConcatTransform(ToScreen(current->id));
219 if (dest)
220 transform->ConcatTransform(FromScreen(dest->id));
221 return;
222 }
223
224 // Flattening is defined in a way that requires it to be applied while
225 // traversing downward in the tree. We first identify nodes that are on the
226 // path from the source to the destination (this is traversing upward), and
227 // then we visit these nodes in reverse order, flattening as needed. We
228 // early-out if we get to a node whose target node is the destination, since
229 // we can then re-use the target space transform stored at that node. However,
230 // we cannot re-use a stored target space transform if the destination has a
231 // zero surface contents scale, since stored target space transforms have
232 // surface contents scale baked in, but we need to compute an unscaled
233 // transform.
234 std::vector<int> source_to_destination;
235 source_to_destination.push_back(current->id);
236 current = parent(current);
237 for (; current && current->id > dest_id; current = parent(current))
238 source_to_destination.push_back(current->id);
239
240 gfx::Transform combined_transform;
241 if (current->id < dest_id) {
242 // We have reached the lowest common ancestor of the source and destination
243 // nodes. This case can occur when we are transforming between a node
244 // corresponding to a fixed-position layer (or its descendant) and the node
245 // corresponding to the layer's render target. For example, consider the
246 // layer tree R->T->S->F where F is fixed-position, S owns a render surface,
247 // and T has a significant transform. This will yield the following
248 // transform tree:
249 // R
250 // |
251 // T
252 // /|
253 // S F
254 // In this example, T will have id 2, S will have id 3, and F will have id
255 // 4. When walking up the ancestor chain from F, the first node with a
256 // smaller id than S will be T, the lowest common ancestor of these nodes.
257 // We compute the transform from T to S here, and then from F to T in the
258 // loop below.
259 DCHECK(IsDescendant(dest_id, current->id));
260 CombineInversesBetween(current->id, dest_id, &combined_transform);
261 DCHECK(combined_transform.IsApproximatelyIdentityOrTranslation(
262 SkDoubleToScalar(1e-4)));
263 }
264
265 size_t source_to_destination_size = source_to_destination.size();
266 for (size_t i = 0; i < source_to_destination_size; ++i) {
267 size_t index = source_to_destination_size - 1 - i;
268 const TransformNode* node = Node(source_to_destination[index]);
269 if (node->flattens_inherited_transform)
270 combined_transform.FlattenTo2d();
271 combined_transform.PreconcatTransform(node->to_parent);
272 }
273
274 transform->ConcatTransform(combined_transform);
275 }
276
CombineInversesBetween(int source_id,int dest_id,gfx::Transform * transform) const277 bool TransformTree::CombineInversesBetween(int source_id,
278 int dest_id,
279 gfx::Transform* transform) const {
280 DCHECK(source_id < dest_id);
281 const TransformNode* current = Node(dest_id);
282 const TransformNode* dest = Node(source_id);
283 // Just as in CombineTransformsBetween, we can use screen space transforms in
284 // this computation only when there isn't any non-trivial flattening
285 // involved.
286 if (current->ancestors_are_invertible &&
287 current->node_and_ancestors_are_flat) {
288 transform->PreconcatTransform(FromScreen(current->id));
289 if (dest)
290 transform->PreconcatTransform(ToScreen(dest->id));
291 return true;
292 }
293
294 // Inverting a flattening is not equivalent to flattening an inverse. This
295 // means we cannot, for example, use the inverse of each node's to_parent
296 // transform, flattening where needed. Instead, we must compute the transform
297 // from the destination to the source, with flattening, and then invert the
298 // result.
299 gfx::Transform dest_to_source;
300 CombineTransformsBetween(dest_id, source_id, &dest_to_source);
301 gfx::Transform source_to_dest;
302 bool all_are_invertible = dest_to_source.GetInverse(&source_to_dest);
303 transform->PreconcatTransform(source_to_dest);
304 return all_are_invertible;
305 }
306
307 // This function should match the offset we set for sticky position layer in
308 // blink::LayoutBoxModelObject::StickyPositionOffset.
StickyPositionOffset(TransformNode * node)309 gfx::Vector2dF TransformTree::StickyPositionOffset(TransformNode* node) {
310 StickyPositionNodeData* sticky_data = MutableStickyPositionData(node->id);
311 if (!sticky_data)
312 return gfx::Vector2dF();
313 const StickyPositionConstraint& constraint = sticky_data->constraints;
314 ScrollNode* scroll_node =
315 property_trees()->scroll_tree.Node(sticky_data->scroll_ancestor);
316 TransformNode* transform_node = Node(scroll_node->transform_id);
317 const auto& scroll_offset = transform_node->scroll_offset;
318 DCHECK(property_trees()->scroll_tree.current_scroll_offset(
319 scroll_node->element_id) == scroll_offset);
320 gfx::PointF scroll_position(scroll_offset.x(), scroll_offset.y());
321 if (transform_node->scrolls) {
322 // The scroll position does not include snapping which shifts the scroll
323 // offset to align to a pixel boundary, we need to manually include it here.
324 // In this case, snapping is caused by a scroll.
325 scroll_position -= transform_node->snap_amount;
326 }
327
328 gfx::RectF clip = constraint.constraint_box_rect;
329 clip.Offset(scroll_position.x(), scroll_position.y());
330
331 // The clip region may need to be offset by the outer viewport bounds, e.g. if
332 // the top bar hides/shows. Position sticky should never attach to the inner
333 // viewport since it shouldn't be affected by pinch-zoom.
334 DCHECK(!scroll_node->scrolls_inner_viewport);
335 if (scroll_node->scrolls_outer_viewport) {
336 clip.set_width(
337 clip.width() +
338 property_trees()->outer_viewport_container_bounds_delta().x());
339 clip.set_height(
340 clip.height() +
341 property_trees()->outer_viewport_container_bounds_delta().y());
342 }
343
344 gfx::Vector2dF ancestor_sticky_box_offset;
345 if (sticky_data->nearest_node_shifting_sticky_box !=
346 TransformTree::kInvalidNodeId) {
347 // TODO(crbug.com/1128479): Investigate why there would be an invalid index
348 // passed in. Early return for now.
349 if (sticky_data->nearest_node_shifting_sticky_box >=
350 static_cast<int>(property_trees()->transform_tree.size()))
351 return gfx::Vector2dF();
352 const StickyPositionNodeData* ancestor_sticky_data =
353 GetStickyPositionData(sticky_data->nearest_node_shifting_sticky_box);
354 DCHECK(ancestor_sticky_data);
355 ancestor_sticky_box_offset =
356 ancestor_sticky_data->total_sticky_box_sticky_offset;
357 }
358
359 gfx::Vector2dF ancestor_containing_block_offset;
360 if (sticky_data->nearest_node_shifting_containing_block !=
361 TransformTree::kInvalidNodeId) {
362 // TODO(crbug.com/1128479): Investigate why there would be an invalid index
363 // passed in. Early return for now.
364 if (sticky_data->nearest_node_shifting_containing_block >=
365 static_cast<int>(property_trees()->transform_tree.size()))
366 return gfx::Vector2dF();
367 const StickyPositionNodeData* ancestor_sticky_data = GetStickyPositionData(
368 sticky_data->nearest_node_shifting_containing_block);
369 DCHECK(ancestor_sticky_data);
370 ancestor_containing_block_offset =
371 ancestor_sticky_data->total_containing_block_sticky_offset;
372 }
373
374 // Compute the current position of the constraint rects based on the original
375 // positions and the offsets from ancestor sticky elements.
376 gfx::RectF sticky_box_rect =
377 gfx::RectF(constraint.scroll_container_relative_sticky_box_rect) +
378 ancestor_sticky_box_offset + ancestor_containing_block_offset;
379 gfx::RectF containing_block_rect =
380 gfx::RectF(constraint.scroll_container_relative_containing_block_rect) +
381 ancestor_containing_block_offset;
382
383 gfx::Vector2dF sticky_offset;
384
385 // In each of the following cases, we measure the limit which is the point
386 // that the element should stick to, clamping on one side to 0 (because sticky
387 // only pushes elements in one direction). Then we clamp to how far we can
388 // push the element in that direction without being pushed outside of its
389 // containing block.
390 //
391 // Note: The order of applying the sticky constraints is applied such that
392 // left offset takes precedence over right offset, and top takes precedence
393 // over bottom offset.
394 if (constraint.is_anchored_right) {
395 float right_limit = clip.right() - constraint.right_offset;
396 float right_delta =
397 std::min<float>(0, right_limit - sticky_box_rect.right());
398 float available_space =
399 std::min<float>(0, containing_block_rect.x() - sticky_box_rect.x());
400 if (right_delta < available_space)
401 right_delta = available_space;
402 sticky_offset.set_x(sticky_offset.x() + right_delta);
403 }
404 if (constraint.is_anchored_left) {
405 float left_limit = clip.x() + constraint.left_offset;
406 float left_delta = std::max<float>(0, left_limit - sticky_box_rect.x());
407 float available_space = std::max<float>(
408 0, containing_block_rect.right() - sticky_box_rect.right());
409 if (left_delta > available_space)
410 left_delta = available_space;
411 sticky_offset.set_x(sticky_offset.x() + left_delta);
412 }
413 if (constraint.is_anchored_bottom) {
414 float bottom_limit = clip.bottom() - constraint.bottom_offset;
415 float bottom_delta =
416 std::min<float>(0, bottom_limit - sticky_box_rect.bottom());
417 float available_space =
418 std::min<float>(0, containing_block_rect.y() - sticky_box_rect.y());
419 if (bottom_delta < available_space)
420 bottom_delta = available_space;
421 sticky_offset.set_y(sticky_offset.y() + bottom_delta);
422 }
423 if (constraint.is_anchored_top) {
424 float top_limit = clip.y() + constraint.top_offset;
425 float top_delta = std::max<float>(0, top_limit - sticky_box_rect.y());
426 float available_space = std::max<float>(
427 0, containing_block_rect.bottom() - sticky_box_rect.bottom());
428 if (top_delta > available_space)
429 top_delta = available_space;
430 sticky_offset.set_y(sticky_offset.y() + top_delta);
431 }
432
433 sticky_data->total_sticky_box_sticky_offset =
434 ancestor_sticky_box_offset + sticky_offset;
435 sticky_data->total_containing_block_sticky_offset =
436 ancestor_sticky_box_offset + ancestor_containing_block_offset +
437 sticky_offset;
438
439 // return
440 return gfx::Vector2dF(roundf(sticky_offset.x()), roundf(sticky_offset.y()));
441 }
442
UpdateLocalTransform(TransformNode * node)443 void TransformTree::UpdateLocalTransform(TransformNode* node) {
444 gfx::Transform transform;
445 transform.Translate3d(node->post_translation.x() + node->origin.x(),
446 node->post_translation.y() + node->origin.y(),
447 node->origin.z());
448
449 float fixed_position_adjustment = 0;
450 if (node->moved_by_outer_viewport_bounds_delta_y) {
451 fixed_position_adjustment =
452 property_trees()->outer_viewport_container_bounds_delta().y();
453 }
454
455 transform.Translate(-node->scroll_offset.x(),
456 -node->scroll_offset.y() + fixed_position_adjustment);
457 transform.Translate(StickyPositionOffset(node));
458 transform.PreconcatTransform(node->local);
459 transform.Translate3d(gfx::Point3F() - node->origin);
460
461 node->set_to_parent(transform);
462 node->needs_local_transform_update = false;
463 }
464
UpdateScreenSpaceTransform(TransformNode * node,TransformNode * parent_node)465 void TransformTree::UpdateScreenSpaceTransform(TransformNode* node,
466 TransformNode* parent_node) {
467 DCHECK(parent_node);
468 gfx::Transform to_screen_space_transform = ToScreen(parent_node->id);
469 if (node->flattens_inherited_transform)
470 to_screen_space_transform.FlattenTo2d();
471 to_screen_space_transform.PreconcatTransform(node->to_parent);
472 node->ancestors_are_invertible = parent_node->ancestors_are_invertible;
473 node->node_and_ancestors_are_flat =
474 parent_node->node_and_ancestors_are_flat && node->to_parent.IsFlat();
475 SetToScreen(node->id, to_screen_space_transform);
476
477 gfx::Transform from_screen;
478 if (!ToScreen(node->id).GetInverse(&from_screen))
479 node->ancestors_are_invertible = false;
480 SetFromScreen(node->id, from_screen);
481 }
482
UpdateAnimationProperties(TransformNode * node,TransformNode * parent_node)483 void TransformTree::UpdateAnimationProperties(TransformNode* node,
484 TransformNode* parent_node) {
485 DCHECK(parent_node);
486 bool ancestor_is_animating = false;
487 ancestor_is_animating = parent_node->to_screen_is_potentially_animated;
488 node->to_screen_is_potentially_animated =
489 node->has_potential_animation || ancestor_is_animating;
490 }
491
UndoSnapping(TransformNode * node)492 void TransformTree::UndoSnapping(TransformNode* node) {
493 // to_parent transform has snapping from previous frame baked in.
494 // We need to undo it and use the un-snapped transform to compute current
495 // target and screen space transforms.
496 node->to_parent.Translate(-node->snap_amount.x(), -node->snap_amount.y());
497 }
498
UpdateSnapping(TransformNode * node)499 void TransformTree::UpdateSnapping(TransformNode* node) {
500 if (!node->should_be_snapped || node->to_screen_is_potentially_animated ||
501 !ToScreen(node->id).IsScaleOrTranslation() ||
502 !node->ancestors_are_invertible) {
503 return;
504 }
505
506 // Snapping must be done in target space (the pixels we care about) and then
507 // the render pass should also be snapped if necessary. But, we do it in
508 // screen space because it is easier and works most of the time if there is
509 // no intermediate render pass with a snap-destrying transform. If ST is the
510 // screen space transform and ST' is ST with its translation components
511 // rounded, then what we're after is the scroll delta X, where ST * X = ST'.
512 // I.e., we want a transform that will realize our snap. It follows that
513 // X = ST^-1 * ST'. We cache ST and ST^-1 to make this more efficient.
514 DCHECK_LT(node->id, static_cast<int>(cached_data_.size()));
515 gfx::Transform& to_screen = cached_data_[node->id].to_screen;
516 to_screen.RoundTranslationComponents();
517 gfx::Transform& from_screen = cached_data_[node->id].from_screen;
518 gfx::Transform delta = from_screen;
519 delta *= to_screen;
520
521 constexpr float kTolerance = 1e-4f;
522 DCHECK(delta.IsApproximatelyIdentityOrTranslation(kTolerance))
523 << delta.ToString();
524
525 gfx::Vector2dF translation = delta.To2dTranslation();
526 node->snap_amount = translation;
527 if (translation.IsZero())
528 return;
529
530 from_screen.matrix().postTranslate(-translation.x(), -translation.y(), 0);
531 node->to_parent.Translate(translation.x(), translation.y());
532 // Avoid accumulation of errors in to_parent.
533 if (node->to_parent.IsApproximatelyIdentityOrIntegerTranslation(kTolerance))
534 node->to_parent.RoundTranslationComponents();
535 }
536
UpdateTransformChanged(TransformNode * node,TransformNode * parent_node)537 void TransformTree::UpdateTransformChanged(TransformNode* node,
538 TransformNode* parent_node) {
539 DCHECK(parent_node);
540 if (parent_node->transform_changed)
541 node->transform_changed = true;
542 }
543
UpdateNodeAndAncestorsAreAnimatedOrInvertible(TransformNode * node,TransformNode * parent_node)544 void TransformTree::UpdateNodeAndAncestorsAreAnimatedOrInvertible(
545 TransformNode* node,
546 TransformNode* parent_node) {
547 DCHECK(parent_node);
548 if (!parent_node->node_and_ancestors_are_animated_or_invertible) {
549 node->node_and_ancestors_are_animated_or_invertible = false;
550 return;
551 }
552 bool is_invertible = node->is_invertible;
553 // Even when the current node's transform and the parent's screen space
554 // transform are invertible, the current node's screen space transform can
555 // become uninvertible due to floating-point arithmetic.
556 if (!node->ancestors_are_invertible && parent_node->ancestors_are_invertible)
557 is_invertible = false;
558 node->node_and_ancestors_are_animated_or_invertible =
559 node->has_potential_animation || is_invertible;
560 }
561
UpdateNodeOrAncestorsWillChangeTransform(TransformNode * node,TransformNode * parent_node)562 void TransformTree::UpdateNodeOrAncestorsWillChangeTransform(
563 TransformNode* node,
564 TransformNode* parent_node) {
565 node->node_or_ancestors_will_change_transform = node->will_change_transform;
566 if (parent_node) {
567 node->node_or_ancestors_will_change_transform |=
568 parent_node->node_or_ancestors_will_change_transform;
569 }
570 }
571
SetRootScaleAndTransform(float device_scale_factor,const gfx::Transform & device_transform)572 void TransformTree::SetRootScaleAndTransform(
573 float device_scale_factor,
574 const gfx::Transform& device_transform) {
575 device_scale_factor_ = device_scale_factor;
576 gfx::Vector2dF device_transform_scale_components =
577 MathUtil::ComputeTransform2dScaleComponents(device_transform, 1.f);
578
579 // Not handling the rare case of different x and y device scale.
580 device_transform_scale_factor_ =
581 std::max(device_transform_scale_components.x(),
582 device_transform_scale_components.y());
583
584 // Let DT be the device transform and DSF be the matrix scaled by (device
585 // scale factor * page scale factor for root). Let Screen Space Scale(SSS) =
586 // scale component of DT*DSF. The screen space transform of the root
587 // transform node is set to SSS and the post local transform of the contents
588 // root node is set to SSS^-1*DT*DSF.
589 gfx::Transform transform = device_transform;
590 transform.Scale(device_scale_factor, device_scale_factor);
591 gfx::Vector2dF screen_space_scale =
592 MathUtil::ComputeTransform2dScaleComponents(transform,
593 device_scale_factor);
594 DCHECK_NE(screen_space_scale.x(), 0.f);
595 DCHECK_NE(screen_space_scale.y(), 0.f);
596
597 gfx::Transform root_to_screen;
598 root_to_screen.Scale(screen_space_scale.x(), screen_space_scale.y());
599 gfx::Transform root_from_screen;
600 bool invertible = root_to_screen.GetInverse(&root_from_screen);
601 DCHECK(invertible);
602 if (root_to_screen != ToScreen(kRootNodeId)) {
603 SetToScreen(kRootNodeId, root_to_screen);
604 SetFromScreen(kRootNodeId, root_from_screen);
605 set_needs_update(true);
606 }
607
608 transform.ConcatTransform(root_from_screen);
609 TransformNode* contents_root_node = Node(kContentsRootNodeId);
610 if (contents_root_node->local != transform) {
611 contents_root_node->local = transform;
612 contents_root_node->needs_local_transform_update = true;
613 set_needs_update(true);
614 }
615 }
616
UpdateOuterViewportContainerBoundsDelta()617 void TransformTree::UpdateOuterViewportContainerBoundsDelta() {
618 if (nodes_affected_by_outer_viewport_bounds_delta_.empty())
619 return;
620
621 set_needs_update(true);
622 for (int i : nodes_affected_by_outer_viewport_bounds_delta_)
623 Node(i)->needs_local_transform_update = true;
624 }
625
AddNodeAffectedByOuterViewportBoundsDelta(int node_id)626 void TransformTree::AddNodeAffectedByOuterViewportBoundsDelta(int node_id) {
627 nodes_affected_by_outer_viewport_bounds_delta_.push_back(node_id);
628 }
629
HasNodesAffectedByOuterViewportBoundsDelta() const630 bool TransformTree::HasNodesAffectedByOuterViewportBoundsDelta() const {
631 return !nodes_affected_by_outer_viewport_bounds_delta_.empty();
632 }
633
FromScreen(int node_id) const634 const gfx::Transform& TransformTree::FromScreen(int node_id) const {
635 DCHECK(static_cast<int>(cached_data_.size()) > node_id);
636 return cached_data_[node_id].from_screen;
637 }
638
SetFromScreen(int node_id,const gfx::Transform & transform)639 void TransformTree::SetFromScreen(int node_id,
640 const gfx::Transform& transform) {
641 DCHECK(static_cast<int>(cached_data_.size()) > node_id);
642 cached_data_[node_id].from_screen = transform;
643 }
644
ToScreen(int node_id) const645 const gfx::Transform& TransformTree::ToScreen(int node_id) const {
646 DCHECK(static_cast<int>(cached_data_.size()) > node_id);
647 return cached_data_[node_id].to_screen;
648 }
649
SetToScreen(int node_id,const gfx::Transform & transform)650 void TransformTree::SetToScreen(int node_id, const gfx::Transform& transform) {
651 DCHECK(static_cast<int>(cached_data_.size()) > node_id);
652 cached_data_[node_id].to_screen = transform;
653 cached_data_[node_id].is_showing_backface = transform.IsBackFaceVisible();
654 }
655
656 #if DCHECK_IS_ON()
operator ==(const TransformTree & other) const657 bool TransformTree::operator==(const TransformTree& other) const {
658 return PropertyTree::operator==(other) &&
659 page_scale_factor_ == other.page_scale_factor() &&
660 device_scale_factor_ == other.device_scale_factor() &&
661 device_transform_scale_factor_ ==
662 other.device_transform_scale_factor() &&
663 nodes_affected_by_outer_viewport_bounds_delta_ ==
664 other.nodes_affected_by_outer_viewport_bounds_delta() &&
665 cached_data_ == other.cached_data();
666 }
667 #endif
668
MutableStickyPositionData(int node_id)669 StickyPositionNodeData* TransformTree::MutableStickyPositionData(int node_id) {
670 const TransformNode* node = Node(node_id);
671 if (node->sticky_position_constraint_id == -1)
672 return nullptr;
673 return &sticky_position_data_[node->sticky_position_constraint_id];
674 }
675
EnsureStickyPositionData(int node_id)676 StickyPositionNodeData& TransformTree::EnsureStickyPositionData(int node_id) {
677 TransformNode* node = Node(node_id);
678 if (node->sticky_position_constraint_id == -1) {
679 node->sticky_position_constraint_id = sticky_position_data_.size();
680 sticky_position_data_.push_back(StickyPositionNodeData());
681 }
682 return sticky_position_data_[node->sticky_position_constraint_id];
683 }
684
EffectTree()685 EffectTree::EffectTree() {
686 render_surfaces_.push_back(nullptr);
687 }
688
689 EffectTree::~EffectTree() = default;
690
Insert(const EffectNode & tree_node,int parent_id)691 int EffectTree::Insert(const EffectNode& tree_node, int parent_id) {
692 int node_id = PropertyTree<EffectNode>::Insert(tree_node, parent_id);
693 DCHECK_EQ(node_id, static_cast<int>(render_surfaces_.size()));
694
695 render_surfaces_.push_back(nullptr);
696 return node_id;
697 }
698
clear()699 void EffectTree::clear() {
700 PropertyTree<EffectNode>::clear();
701 render_surfaces_.clear();
702 render_surfaces_.push_back(nullptr);
703
704 #if DCHECK_IS_ON()
705 EffectTree tree;
706 DCHECK(tree == *this);
707 #endif
708 }
709
EffectiveOpacity(const EffectNode * node) const710 float EffectTree::EffectiveOpacity(const EffectNode* node) const {
711 return node->subtree_hidden ? 0.f : node->opacity;
712 }
713
UpdateOpacities(EffectNode * node,EffectNode * parent_node)714 void EffectTree::UpdateOpacities(EffectNode* node, EffectNode* parent_node) {
715 node->screen_space_opacity = EffectiveOpacity(node);
716
717 if (parent_node)
718 node->screen_space_opacity *= parent_node->screen_space_opacity;
719 }
720
UpdateSubtreeHidden(EffectNode * node,EffectNode * parent_node)721 void EffectTree::UpdateSubtreeHidden(EffectNode* node,
722 EffectNode* parent_node) {
723 if (parent_node)
724 node->subtree_hidden |= parent_node->subtree_hidden;
725 }
726
UpdateIsDrawn(EffectNode * node,EffectNode * parent_node)727 void EffectTree::UpdateIsDrawn(EffectNode* node, EffectNode* parent_node) {
728 // Nodes that have screen space opacity 0 are hidden. So they are not drawn.
729 // Exceptions:
730 // 1) Nodes that contribute to copy requests, whether hidden or not, must be
731 // drawn.
732 // 2) Nodes that have a backdrop filter.
733 // 3) Nodes with animating screen space opacity on main thread or pending tree
734 // are drawn if their parent is drawn irrespective of their opacity.
735 if (node->has_copy_request || node->cache_render_surface)
736 node->is_drawn = true;
737 else if (EffectiveOpacity(node) == 0.f &&
738 (!node->has_potential_opacity_animation ||
739 property_trees()->is_active) &&
740 node->backdrop_filters.IsEmpty())
741 node->is_drawn = false;
742 else if (parent_node)
743 node->is_drawn = parent_node->is_drawn;
744 else
745 node->is_drawn = true;
746 }
747
UpdateEffectChanged(EffectNode * node,EffectNode * parent_node)748 void EffectTree::UpdateEffectChanged(EffectNode* node,
749 EffectNode* parent_node) {
750 if (parent_node && parent_node->effect_changed) {
751 node->effect_changed = true;
752 }
753 }
754
UpdateHasFilters(EffectNode * node,EffectNode * parent_node)755 void EffectTree::UpdateHasFilters(EffectNode* node, EffectNode* parent_node) {
756 node->node_or_ancestor_has_filters =
757 !node->filters.IsEmpty() || node->has_potential_filter_animation;
758 if (parent_node) {
759 node->node_or_ancestor_has_filters |=
760 parent_node->node_or_ancestor_has_filters;
761 }
762 }
763
UpdateBackfaceVisibility(EffectNode * node,EffectNode * parent_node)764 void EffectTree::UpdateBackfaceVisibility(EffectNode* node,
765 EffectNode* parent_node) {
766 if (parent_node && parent_node->hidden_by_backface_visibility) {
767 node->hidden_by_backface_visibility = true;
768 return;
769 }
770 if (node->double_sided) {
771 node->hidden_by_backface_visibility = false;
772 return;
773 }
774 node->hidden_by_backface_visibility =
775 property_trees()
776 ->transform_tree.cached_data()[node->transform_id]
777 .is_showing_backface;
778 }
779
UpdateHasMaskingChild(EffectNode * node,EffectNode * parent_node)780 void EffectTree::UpdateHasMaskingChild(EffectNode* node,
781 EffectNode* parent_node) {
782 // Reset to false when a node is first met. We'll set the bit later
783 // when we actually encounter a masking child.
784 node->has_masking_child = false;
785 if (node->blend_mode == SkBlendMode::kDstIn) {
786 parent_node->has_masking_child = true;
787 }
788 }
789
UpdateOnlyDrawsVisibleContent(EffectNode * node,EffectNode * parent_node)790 void EffectTree::UpdateOnlyDrawsVisibleContent(EffectNode* node,
791 EffectNode* parent_node) {
792 node->only_draws_visible_content = !node->has_copy_request;
793 if (parent_node)
794 node->only_draws_visible_content &= parent_node->only_draws_visible_content;
795 if (!node->backdrop_filters.IsEmpty()) {
796 node->only_draws_visible_content &=
797 !node->backdrop_filters.HasFilterOfType(FilterOperation::ZOOM);
798 }
799 }
800
UpdateSurfaceContentsScale(EffectNode * effect_node)801 void EffectTree::UpdateSurfaceContentsScale(EffectNode* effect_node) {
802 if (!effect_node->HasRenderSurface()) {
803 effect_node->surface_contents_scale = gfx::Vector2dF(1.0f, 1.0f);
804 return;
805 }
806
807 TransformTree& transform_tree = property_trees()->transform_tree;
808 float layer_scale_factor = transform_tree.device_scale_factor() *
809 transform_tree.device_transform_scale_factor();
810 TransformNode* transform_node =
811 transform_tree.Node(effect_node->transform_id);
812 if (transform_node->in_subtree_of_page_scale_layer)
813 layer_scale_factor *= transform_tree.page_scale_factor();
814
815 const gfx::Vector2dF old_scale = effect_node->surface_contents_scale;
816 effect_node->surface_contents_scale =
817 MathUtil::ComputeTransform2dScaleComponents(
818 transform_tree.ToScreen(transform_node->id), layer_scale_factor);
819
820 // If surface contents scale changes, draw transforms are no longer valid.
821 // Invalidates the draw transform cache and updates the clip for the surface.
822 if (old_scale != effect_node->surface_contents_scale) {
823 property_trees()->clip_tree.set_needs_update(true);
824 property_trees()->UpdateTransformTreeUpdateNumber();
825 }
826 }
827
FindNodeFromElementId(ElementId id)828 EffectNode* EffectTree::FindNodeFromElementId(ElementId id) {
829 auto iterator = property_trees()->element_id_to_effect_node_index.find(id);
830 if (iterator == property_trees()->element_id_to_effect_node_index.end())
831 return nullptr;
832
833 return Node(iterator->second);
834 }
835
OnOpacityAnimated(ElementId id,float opacity)836 bool EffectTree::OnOpacityAnimated(ElementId id, float opacity) {
837 EffectNode* node = FindNodeFromElementId(id);
838 DCHECK(node);
839 if (node->opacity == opacity)
840 return false;
841 node->opacity = opacity;
842 node->effect_changed = true;
843 property_trees()->changed = true;
844 property_trees()->effect_tree.set_needs_update(true);
845 return true;
846 }
847
OnFilterAnimated(ElementId id,const FilterOperations & filters)848 bool EffectTree::OnFilterAnimated(ElementId id,
849 const FilterOperations& filters) {
850 EffectNode* node = FindNodeFromElementId(id);
851 DCHECK(node);
852 if (node->filters == filters)
853 return false;
854 node->filters = filters;
855 node->effect_changed = true;
856 property_trees()->changed = true;
857 property_trees()->effect_tree.set_needs_update(true);
858 return true;
859 }
860
OnBackdropFilterAnimated(ElementId id,const FilterOperations & backdrop_filters)861 bool EffectTree::OnBackdropFilterAnimated(
862 ElementId id,
863 const FilterOperations& backdrop_filters) {
864 EffectNode* node = FindNodeFromElementId(id);
865 DCHECK(node);
866 if (node->backdrop_filters == backdrop_filters)
867 return false;
868 node->backdrop_filters = backdrop_filters;
869 node->effect_changed = true;
870 property_trees()->changed = true;
871 property_trees()->effect_tree.set_needs_update(true);
872 return true;
873 }
874
UpdateEffects(int id)875 void EffectTree::UpdateEffects(int id) {
876 EffectNode* node = Node(id);
877 EffectNode* parent_node = parent(node);
878
879 UpdateOpacities(node, parent_node);
880 UpdateSubtreeHidden(node, parent_node);
881 UpdateIsDrawn(node, parent_node);
882 UpdateEffectChanged(node, parent_node);
883 UpdateHasFilters(node, parent_node);
884 UpdateBackfaceVisibility(node, parent_node);
885 UpdateHasMaskingChild(node, parent_node);
886 UpdateOnlyDrawsVisibleContent(node, parent_node);
887 UpdateSurfaceContentsScale(node);
888 }
889
AddCopyRequest(int node_id,std::unique_ptr<viz::CopyOutputRequest> request)890 void EffectTree::AddCopyRequest(
891 int node_id,
892 std::unique_ptr<viz::CopyOutputRequest> request) {
893 copy_requests_.insert(std::make_pair(node_id, std::move(request)));
894 }
895
PushCopyRequestsTo(EffectTree * other_tree)896 void EffectTree::PushCopyRequestsTo(EffectTree* other_tree) {
897 // If other_tree still has copy requests, this means there was a commit
898 // without a draw. This only happens in some edge cases during lost context or
899 // visibility changes, so don't try to handle preserving these output
900 // requests.
901 if (!other_tree->copy_requests_.empty()) {
902 // Destroying these copy requests will abort them.
903 other_tree->copy_requests_.clear();
904 }
905
906 if (copy_requests_.empty())
907 return;
908
909 for (auto& request : copy_requests_) {
910 other_tree->copy_requests_.insert(
911 std::make_pair(request.first, std::move(request.second)));
912 }
913 copy_requests_.clear();
914
915 // Property trees need to get rebuilt since effect nodes (and render surfaces)
916 // that were created only for the copy requests we just pushed are no longer
917 // needed.
918 if (property_trees()->is_main_thread)
919 property_trees()->needs_rebuild = true;
920 }
921
TakeCopyRequestsAndTransformToSurface(int node_id,std::vector<std::unique_ptr<viz::CopyOutputRequest>> * requests)922 void EffectTree::TakeCopyRequestsAndTransformToSurface(
923 int node_id,
924 std::vector<std::unique_ptr<viz::CopyOutputRequest>>* requests) {
925 EffectNode* effect_node = Node(node_id);
926 DCHECK(effect_node->HasRenderSurface());
927 DCHECK(effect_node->has_copy_request);
928
929 // The area needs to be transformed from the space of content that draws to
930 // the surface to the space of the surface itself.
931 int destination_id = effect_node->transform_id;
932 int source_id;
933 if (effect_node->parent_id != EffectTree::kInvalidNodeId) {
934 // For non-root surfaces, transform only by sub-layer scale.
935 source_id = destination_id;
936 } else {
937 // The root surface doesn't have the notion of sub-layer scale, but instead
938 // has a similar notion of transforming from the space of the root layer to
939 // the space of the screen.
940 DCHECK_EQ(kRootNodeId, destination_id);
941 source_id = TransformTree::kContentsRootNodeId;
942 }
943 gfx::Transform transform;
944 property_trees()->GetToTarget(source_id, node_id, &transform);
945
946 // Move each CopyOutputRequest out of |copy_requests_| and into |requests|,
947 // adjusting the source area and scale ratio of each. If the transform is
948 // something other than a straightforward translate+scale, the copy requests
949 // will be dropped.
950 auto range = copy_requests_.equal_range(node_id);
951 if (transform.IsPositiveScaleOrTranslation()) {
952 // Transform a vector in content space to surface space to determine how the
953 // scale ratio of each CopyOutputRequest should be adjusted. Since the scale
954 // ratios are provided integer coordinates, the basis vector determines the
955 // precision w.r.t. the fractional part of the Transform's scale factors.
956 constexpr gfx::Vector2d kContentVector(1024, 1024);
957 gfx::RectF surface_rect(0, 0, kContentVector.x(), kContentVector.y());
958 transform.TransformRect(&surface_rect);
959
960 for (auto it = range.first; it != range.second; ++it) {
961 viz::CopyOutputRequest* const request = it->second.get();
962 if (request->has_area()) {
963 // Avoid creating bigger copy area which may contain unnecessary
964 // area if the error margin is tiny.
965 constexpr float kEpsilon = 0.001f;
966 request->set_area(MathUtil::MapEnclosingClippedRectIgnoringError(
967 transform, request->area(), kEpsilon));
968 }
969
970 // Only adjust the scale ratio if the request specifies one, or if it
971 // specifies a result selection. Otherwise, the requestor is expecting a
972 // copy of the exact source pixels. If the adjustment to the scale ratio
973 // would produce out-of-range values, drop the copy request.
974 if (request->is_scaled() || request->has_result_selection()) {
975 float scale_from_x_f = request->scale_from().x() * surface_rect.width();
976 float scale_from_y_f =
977 request->scale_from().y() * surface_rect.height();
978 if (std::isnan(scale_from_x_f) ||
979 !base::IsValueInRangeForNumericType<int>(scale_from_x_f) ||
980 std::isnan(scale_from_y_f) ||
981 !base::IsValueInRangeForNumericType<int>(scale_from_y_f)) {
982 continue;
983 }
984 int scale_to_x = request->scale_to().x();
985 int scale_to_y = request->scale_to().y();
986 if (!base::CheckMul(scale_to_x, kContentVector.x())
987 .AssignIfValid(&scale_to_x) ||
988 !base::CheckMul(scale_to_y, kContentVector.y())
989 .AssignIfValid(&scale_to_y)) {
990 continue;
991 }
992 int scale_from_x = base::ClampRound(scale_from_x_f);
993 int scale_from_y = base::ClampRound(scale_from_y_f);
994 if (scale_from_x <= 0 || scale_from_y <= 0 || scale_to_x <= 0 ||
995 scale_to_y <= 0) {
996 // Transformed scaling ratio became illegal. Drop the request to
997 // provide an empty response.
998 continue;
999 }
1000 request->SetScaleRatio(gfx::Vector2d(scale_from_x, scale_from_y),
1001 gfx::Vector2d(scale_to_x, scale_to_y));
1002 }
1003
1004 requests->push_back(std::move(it->second));
1005 }
1006 }
1007 copy_requests_.erase(range.first, range.second);
1008 }
1009
HasCopyRequests() const1010 bool EffectTree::HasCopyRequests() const {
1011 return !copy_requests_.empty();
1012 }
1013
ClearCopyRequests()1014 void EffectTree::ClearCopyRequests() {
1015 for (auto& node : nodes()) {
1016 node.subtree_has_copy_request = false;
1017 node.has_copy_request = false;
1018 node.closest_ancestor_with_copy_request_id = EffectTree::kInvalidNodeId;
1019 }
1020
1021 // Any copy requests that are still left will be aborted (sending an empty
1022 // result) on destruction.
1023 copy_requests_.clear();
1024 set_needs_update(true);
1025 }
1026
LowestCommonAncestorWithRenderSurface(int id_1,int id_2) const1027 int EffectTree::LowestCommonAncestorWithRenderSurface(int id_1,
1028 int id_2) const {
1029 DCHECK(GetRenderSurface(id_1));
1030 DCHECK(GetRenderSurface(id_2));
1031 while (id_1 != id_2) {
1032 if (id_1 < id_2)
1033 id_2 = Node(id_2)->target_id;
1034 else
1035 id_1 = Node(id_1)->target_id;
1036 }
1037
1038 return id_1;
1039 }
1040
ContributesToDrawnSurface(int id)1041 bool EffectTree::ContributesToDrawnSurface(int id) {
1042 // All drawn nodes contribute to drawn surface.
1043 // Exception : Nodes that are hidden and are drawn only for the sake of
1044 // copy requests.
1045 EffectNode* node = Node(id);
1046 EffectNode* parent_node = parent(node);
1047 return node->is_drawn && (!parent_node || parent_node->is_drawn);
1048 }
1049
ResetChangeTracking()1050 void EffectTree::ResetChangeTracking() {
1051 for (int id = EffectTree::kContentsRootNodeId; id < static_cast<int>(size());
1052 ++id) {
1053 Node(id)->effect_changed = false;
1054 if (render_surfaces_[id])
1055 render_surfaces_[id]->ResetPropertyChangedFlags();
1056 }
1057 }
1058
TakeRenderSurfaces(std::vector<std::unique_ptr<RenderSurfaceImpl>> * render_surfaces)1059 void EffectTree::TakeRenderSurfaces(
1060 std::vector<std::unique_ptr<RenderSurfaceImpl>>* render_surfaces) {
1061 for (int id = kContentsRootNodeId; id < static_cast<int>(size()); ++id) {
1062 if (render_surfaces_[id]) {
1063 render_surfaces->push_back(std::move(render_surfaces_[id]));
1064 }
1065 }
1066 }
1067
CreateOrReuseRenderSurfaces(std::vector<std::unique_ptr<RenderSurfaceImpl>> * old_render_surfaces,LayerTreeImpl * layer_tree_impl)1068 bool EffectTree::CreateOrReuseRenderSurfaces(
1069 std::vector<std::unique_ptr<RenderSurfaceImpl>>* old_render_surfaces,
1070 LayerTreeImpl* layer_tree_impl) {
1071 // Make a list of {stable id, node id} pairs for nodes that are supposed to
1072 // have surfaces.
1073 std::vector<std::pair<uint64_t, int>> stable_id_node_id_list;
1074 for (int id = kContentsRootNodeId; id < static_cast<int>(size()); ++id) {
1075 EffectNode* node = Node(id);
1076 if (node->HasRenderSurface()) {
1077 stable_id_node_id_list.push_back(
1078 std::make_pair(node->stable_id, node->id));
1079 }
1080 }
1081
1082 // Sort by stable id so that we can process the two lists cosequentially.
1083 std::sort(stable_id_node_id_list.begin(), stable_id_node_id_list.end());
1084 std::sort(old_render_surfaces->begin(), old_render_surfaces->end(),
1085 [](const std::unique_ptr<RenderSurfaceImpl>& a,
1086 const std::unique_ptr<RenderSurfaceImpl>& b) {
1087 return a->id() < b->id();
1088 });
1089
1090 bool render_surfaces_changed = false;
1091 auto surfaces_list_it = old_render_surfaces->begin();
1092 auto id_list_it = stable_id_node_id_list.begin();
1093 while (surfaces_list_it != old_render_surfaces->end() &&
1094 id_list_it != stable_id_node_id_list.end()) {
1095 if ((*surfaces_list_it)->id() == id_list_it->first) {
1096 int new_node_id = id_list_it->second;
1097 render_surfaces_[new_node_id] = std::move(*surfaces_list_it);
1098 render_surfaces_[new_node_id]->set_effect_tree_index(new_node_id);
1099 surfaces_list_it++;
1100 id_list_it++;
1101 continue;
1102 }
1103
1104 render_surfaces_changed = true;
1105
1106 if ((*surfaces_list_it)->id() > id_list_it->first) {
1107 int new_node_id = id_list_it->second;
1108 render_surfaces_[new_node_id] = std::make_unique<RenderSurfaceImpl>(
1109 layer_tree_impl, id_list_it->first);
1110 render_surfaces_[new_node_id]->set_effect_tree_index(new_node_id);
1111 id_list_it++;
1112 } else {
1113 surfaces_list_it++;
1114 }
1115 }
1116
1117 if (surfaces_list_it != old_render_surfaces->end() ||
1118 id_list_it != stable_id_node_id_list.end()) {
1119 render_surfaces_changed = true;
1120 }
1121
1122 while (id_list_it != stable_id_node_id_list.end()) {
1123 int new_node_id = id_list_it->second;
1124 render_surfaces_[new_node_id] =
1125 std::make_unique<RenderSurfaceImpl>(layer_tree_impl, id_list_it->first);
1126 render_surfaces_[new_node_id]->set_effect_tree_index(new_node_id);
1127 id_list_it++;
1128 }
1129
1130 return render_surfaces_changed;
1131 }
1132
ClippedHitTestRegionIsRectangle(int effect_id) const1133 bool EffectTree::ClippedHitTestRegionIsRectangle(int effect_id) const {
1134 const EffectNode* effect_node = Node(effect_id);
1135 for (; effect_node->id != kContentsRootNodeId;
1136 effect_node = Node(effect_node->target_id)) {
1137 gfx::Transform to_target;
1138 if (!property_trees()->GetToTarget(effect_node->transform_id,
1139 effect_node->target_id, &to_target) ||
1140 !to_target.Preserves2dAxisAlignment())
1141 return false;
1142 }
1143 return true;
1144 }
1145
HitTestMayBeAffectedByMask(int effect_id) const1146 bool EffectTree::HitTestMayBeAffectedByMask(int effect_id) const {
1147 const EffectNode* effect_node = Node(effect_id);
1148 for (; effect_node->id != kContentsRootNodeId;
1149 effect_node = Node(effect_node->parent_id)) {
1150 if (!effect_node->mask_filter_info.IsEmpty() ||
1151 effect_node->has_masking_child)
1152 return true;
1153 }
1154 return false;
1155 }
1156
SetViewportClip(gfx::RectF viewport_rect)1157 void ClipTree::SetViewportClip(gfx::RectF viewport_rect) {
1158 if (size() < 2)
1159 return;
1160 ClipNode* node = Node(1);
1161 if (viewport_rect == node->clip)
1162 return;
1163 node->clip = viewport_rect;
1164 set_needs_update(true);
1165 }
1166
ViewportClip() const1167 gfx::RectF ClipTree::ViewportClip() const {
1168 const size_t min_size = 1;
1169 DCHECK_GT(size(), min_size);
1170 return Node(kViewportNodeId)->clip;
1171 }
1172
1173 #if DCHECK_IS_ON()
operator ==(const ClipTree & other) const1174 bool ClipTree::operator==(const ClipTree& other) const {
1175 return PropertyTree::operator==(other);
1176 }
1177 #endif
1178
operator =(const EffectTree & from)1179 EffectTree& EffectTree::operator=(const EffectTree& from) {
1180 PropertyTree::operator=(from);
1181 render_surfaces_.resize(size());
1182 // copy_requests_ are omitted here, since these need to be moved rather
1183 // than copied or assigned.
1184
1185 return *this;
1186 }
1187
1188 #if DCHECK_IS_ON()
operator ==(const EffectTree & other) const1189 bool EffectTree::operator==(const EffectTree& other) const {
1190 return PropertyTree::operator==(other);
1191 }
1192 #endif
1193
ScrollTree()1194 ScrollTree::ScrollTree()
1195 : currently_scrolling_node_id_(kInvalidNodeId),
1196 scroll_offset_map_(ScrollTree::ScrollOffsetMap()) {}
1197
1198 ScrollTree::~ScrollTree() = default;
1199
operator =(const ScrollTree & from)1200 ScrollTree& ScrollTree::operator=(const ScrollTree& from) {
1201 PropertyTree::operator=(from);
1202 currently_scrolling_node_id_ = kInvalidNodeId;
1203 // Maps for ScrollOffsets/SyncedScrollOffsets are intentionally omitted here
1204 // since we can not directly copy them. Pushing of these updates from main
1205 // currently depends on Layer properties for scroll offset animation changes
1206 // (setting clobber_active_value for scroll offset animations interrupted on
1207 // the main thread) being pushed to impl first.
1208 // |callbacks_| is omitted because it's for the main thread only.
1209 return *this;
1210 }
1211
1212 #if DCHECK_IS_ON()
operator ==(const ScrollTree & other) const1213 bool ScrollTree::operator==(const ScrollTree& other) const {
1214 if (scroll_offset_map_ != other.scroll_offset_map_)
1215 return false;
1216 if (synced_scroll_offset_map_ != other.synced_scroll_offset_map_)
1217 return false;
1218 if (callbacks_.get() != other.callbacks_.get())
1219 return false;
1220
1221 bool is_currently_scrolling_node_equal =
1222 currently_scrolling_node_id_ == other.currently_scrolling_node_id_;
1223
1224 return PropertyTree::operator==(other) && is_currently_scrolling_node_equal;
1225 }
1226
CopyCompleteTreeState(const ScrollTree & other)1227 void ScrollTree::CopyCompleteTreeState(const ScrollTree& other) {
1228 currently_scrolling_node_id_ = other.currently_scrolling_node_id_;
1229 scroll_offset_map_ = other.scroll_offset_map_;
1230 synced_scroll_offset_map_ = other.synced_scroll_offset_map_;
1231 callbacks_ = other.callbacks_;
1232 }
1233 #endif // DCHECK_IS_ON()
1234
FindNodeFromElementId(ElementId id)1235 ScrollNode* ScrollTree::FindNodeFromElementId(ElementId id) {
1236 if (!id)
1237 return nullptr;
1238 auto iterator = property_trees()->element_id_to_scroll_node_index.find(id);
1239 if (iterator == property_trees()->element_id_to_scroll_node_index.end())
1240 return nullptr;
1241
1242 return Node(iterator->second);
1243 }
1244
FindNodeFromElementId(ElementId id) const1245 const ScrollNode* ScrollTree::FindNodeFromElementId(ElementId id) const {
1246 if (!id)
1247 return nullptr;
1248 auto iterator = property_trees()->element_id_to_scroll_node_index.find(id);
1249 if (iterator == property_trees()->element_id_to_scroll_node_index.end())
1250 return nullptr;
1251
1252 return Node(iterator->second);
1253 }
1254
IsComposited(const ScrollNode & node) const1255 bool ScrollTree::IsComposited(const ScrollNode& node) const {
1256 return node.is_composited;
1257 }
1258
clear()1259 void ScrollTree::clear() {
1260 PropertyTree<ScrollNode>::clear();
1261
1262 if (property_trees()->is_main_thread) {
1263 currently_scrolling_node_id_ = kInvalidNodeId;
1264 scroll_offset_map_.clear();
1265 }
1266
1267 #if DCHECK_IS_ON()
1268 ScrollTree tree;
1269 if (property_trees()->is_main_thread) {
1270 tree.callbacks_ = callbacks_;
1271 } else {
1272 DCHECK(scroll_offset_map_.empty());
1273 tree.currently_scrolling_node_id_ = currently_scrolling_node_id_;
1274 tree.synced_scroll_offset_map_ = synced_scroll_offset_map_;
1275 }
1276 DCHECK(tree == *this);
1277 #endif
1278 }
1279
MaxScrollOffset(int scroll_node_id) const1280 gfx::ScrollOffset ScrollTree::MaxScrollOffset(int scroll_node_id) const {
1281 const ScrollNode* scroll_node = Node(scroll_node_id);
1282 gfx::SizeF scroll_bounds = this->scroll_bounds(scroll_node_id);
1283
1284 if (!scroll_node->scrollable || scroll_bounds.IsEmpty())
1285 return gfx::ScrollOffset();
1286
1287 TransformTree& transform_tree = property_trees()->transform_tree;
1288 float scale_factor = 1.f;
1289 if (scroll_node->max_scroll_offset_affected_by_page_scale)
1290 scale_factor = transform_tree.page_scale_factor();
1291
1292 gfx::SizeF scaled_scroll_bounds = gfx::ScaleSize(scroll_bounds, scale_factor);
1293 scaled_scroll_bounds.SetSize(std::floor(scaled_scroll_bounds.width()),
1294 std::floor(scaled_scroll_bounds.height()));
1295
1296 gfx::Size clip_layer_bounds = container_bounds(scroll_node->id);
1297
1298 gfx::ScrollOffset _max_offset(
1299 scaled_scroll_bounds.width() - clip_layer_bounds.width(),
1300 scaled_scroll_bounds.height() - clip_layer_bounds.height());
1301
1302 _max_offset.Scale(1 / scale_factor);
1303 _max_offset.SetToMax(gfx::ScrollOffset());
1304 return _max_offset;
1305 }
1306
scroll_bounds(int scroll_node_id) const1307 gfx::SizeF ScrollTree::scroll_bounds(int scroll_node_id) const {
1308 const ScrollNode* scroll_node = Node(scroll_node_id);
1309 gfx::SizeF bounds(scroll_node->bounds);
1310 if (scroll_node->scrolls_inner_viewport) {
1311 const auto& delta = property_trees()->inner_viewport_scroll_bounds_delta();
1312 bounds.Enlarge(delta.x(), delta.y());
1313 }
1314 return bounds;
1315 }
1316
OnScrollOffsetAnimated(ElementId id,int scroll_tree_index,const gfx::ScrollOffset & scroll_offset,LayerTreeImpl * layer_tree_impl)1317 void ScrollTree::OnScrollOffsetAnimated(ElementId id,
1318 int scroll_tree_index,
1319 const gfx::ScrollOffset& scroll_offset,
1320 LayerTreeImpl* layer_tree_impl) {
1321 // Only active tree needs to be updated, pending tree will find out about
1322 // these changes as a result of the shared SyncedProperty.
1323 if (!property_trees()->is_active)
1324 return;
1325
1326 TRACE_EVENT2("cc", "ScrollTree::OnScrollOffsetAnimated", "x",
1327 scroll_offset.x(), "y", scroll_offset.y());
1328 ScrollNode* scroll_node = Node(scroll_tree_index);
1329 if (SetScrollOffset(id,
1330 ClampScrollOffsetToLimits(scroll_offset, *scroll_node)))
1331 layer_tree_impl->DidUpdateScrollOffset(id);
1332 layer_tree_impl->DidAnimateScrollOffset();
1333 }
1334
container_bounds(int scroll_node_id) const1335 gfx::Size ScrollTree::container_bounds(int scroll_node_id) const {
1336 const ScrollNode* scroll_node = Node(scroll_node_id);
1337 gfx::Size container_bounds = scroll_node->container_bounds;
1338
1339 gfx::Vector2dF container_bounds_delta;
1340 if (scroll_node->scrolls_inner_viewport) {
1341 container_bounds_delta.Add(
1342 property_trees()->inner_viewport_container_bounds_delta());
1343 } else if (scroll_node->scrolls_outer_viewport) {
1344 container_bounds_delta.Add(
1345 property_trees()->outer_viewport_container_bounds_delta());
1346 }
1347
1348 gfx::Vector2d delta = gfx::ToCeiledVector2d(container_bounds_delta);
1349 container_bounds.Enlarge(delta.x(), delta.y());
1350
1351 return container_bounds;
1352 }
1353
CurrentlyScrollingNode()1354 ScrollNode* ScrollTree::CurrentlyScrollingNode() {
1355 ScrollNode* scroll_node = Node(currently_scrolling_node_id_);
1356 return scroll_node;
1357 }
1358
CurrentlyScrollingNode() const1359 const ScrollNode* ScrollTree::CurrentlyScrollingNode() const {
1360 const ScrollNode* scroll_node = Node(currently_scrolling_node_id_);
1361 return scroll_node;
1362 }
1363
1364 #if DCHECK_IS_ON()
CurrentlyScrollingNodeId() const1365 int ScrollTree::CurrentlyScrollingNodeId() const {
1366 return currently_scrolling_node_id_;
1367 }
1368 #endif
1369
set_currently_scrolling_node(int scroll_node_id)1370 void ScrollTree::set_currently_scrolling_node(int scroll_node_id) {
1371 currently_scrolling_node_id_ = scroll_node_id;
1372 }
1373
ScreenSpaceTransform(int scroll_node_id) const1374 gfx::Transform ScrollTree::ScreenSpaceTransform(int scroll_node_id) const {
1375 const ScrollNode* scroll_node = Node(scroll_node_id);
1376 const TransformTree& transform_tree = property_trees()->transform_tree;
1377 const TransformNode* transform_node =
1378 transform_tree.Node(scroll_node->transform_id);
1379 gfx::Transform screen_space_transform(
1380 1, 0, 0, 1, scroll_node->offset_to_transform_parent.x(),
1381 scroll_node->offset_to_transform_parent.y());
1382 screen_space_transform.ConcatTransform(
1383 transform_tree.ToScreen(transform_node->id));
1384 if (scroll_node->should_flatten)
1385 screen_space_transform.FlattenTo2d();
1386 return screen_space_transform;
1387 }
1388
GetOrCreateSyncedScrollOffset(ElementId id)1389 SyncedScrollOffset* ScrollTree::GetOrCreateSyncedScrollOffset(ElementId id) {
1390 DCHECK(!property_trees()->is_main_thread);
1391 if (synced_scroll_offset_map_.find(id) == synced_scroll_offset_map_.end()) {
1392 synced_scroll_offset_map_[id] = new SyncedScrollOffset;
1393 }
1394 return synced_scroll_offset_map_[id].get();
1395 }
1396
GetSyncedScrollOffset(ElementId id) const1397 const SyncedScrollOffset* ScrollTree::GetSyncedScrollOffset(
1398 ElementId id) const {
1399 DCHECK(!property_trees()->is_main_thread);
1400 auto it = synced_scroll_offset_map_.find(id);
1401 return it != synced_scroll_offset_map_.end() ? it->second.get() : nullptr;
1402 }
1403
ClampScrollToMaxScrollOffset(const ScrollNode & node,LayerTreeImpl * layer_tree_impl)1404 gfx::Vector2dF ScrollTree::ClampScrollToMaxScrollOffset(
1405 const ScrollNode& node,
1406 LayerTreeImpl* layer_tree_impl) {
1407 gfx::ScrollOffset old_offset = current_scroll_offset(node.element_id);
1408 gfx::ScrollOffset clamped_offset =
1409 ClampScrollOffsetToLimits(old_offset, node);
1410 gfx::Vector2dF delta = clamped_offset.DeltaFrom(old_offset);
1411 if (!delta.IsZero())
1412 ScrollBy(node, delta, layer_tree_impl);
1413 return delta;
1414 }
1415
current_scroll_offset(ElementId id) const1416 const gfx::ScrollOffset ScrollTree::current_scroll_offset(ElementId id) const {
1417 if (property_trees()->is_main_thread) {
1418 auto it = scroll_offset_map_.find(id);
1419 return it != scroll_offset_map_.end() ? it->second : gfx::ScrollOffset();
1420 }
1421 return GetSyncedScrollOffset(id)
1422 ? GetSyncedScrollOffset(id)->Current(property_trees()->is_active)
1423 : gfx::ScrollOffset();
1424 }
1425
GetPixelSnappedScrollOffset(int scroll_node_id) const1426 const gfx::ScrollOffset ScrollTree::GetPixelSnappedScrollOffset(
1427 int scroll_node_id) const {
1428 const ScrollNode* scroll_node = Node(scroll_node_id);
1429 DCHECK(scroll_node);
1430 gfx::ScrollOffset offset = current_scroll_offset(scroll_node->element_id);
1431
1432 const TransformNode* transform_node =
1433 property_trees()->transform_tree.Node(scroll_node->transform_id);
1434 DCHECK(offset == transform_node->scroll_offset)
1435 << "Transform node scroll offset does not match the actual offset, this "
1436 "means the snapped_amount calculation will be incorrect";
1437
1438 if (transform_node->scrolls) {
1439 // If necessary perform a update for this node to ensure snap amount is
1440 // accurate. This method is used by scroll timeline, so it is possible for
1441 // it to get called before transform tree has gone through a full update
1442 // cycle so this node snap amount may be stale.
1443 if (transform_node->needs_local_transform_update)
1444 property_trees()->transform_tree.UpdateTransforms(transform_node->id);
1445
1446 // The calculated pixel snap amount can be slightly larger than the actual
1447 // snapping needed, due to floating point precision errors. In general this
1448 // is fine, but we never want to report a negative scroll offset so avoid
1449 // that case here.
1450 // TODO(crbug.com/1076878): Remove the clamping when scroll timeline effects
1451 // always match the snapping.
1452 offset = ClampScrollOffsetToLimits(
1453 offset - gfx::ScrollOffset(transform_node->snap_amount), *scroll_node);
1454 }
1455
1456 return offset;
1457 }
1458
PullDeltaForMainThread(SyncedScrollOffset * scroll_offset,bool use_fractional_deltas)1459 gfx::ScrollOffset ScrollTree::PullDeltaForMainThread(
1460 SyncedScrollOffset* scroll_offset,
1461 bool use_fractional_deltas) {
1462 DCHECK(property_trees()->is_active);
1463
1464 // Once this setting is enabled, all the complicated rounding logic below can
1465 // go away.
1466 if (use_fractional_deltas)
1467 return scroll_offset->PullDeltaForMainThread();
1468
1469 // TODO(flackr): We should pass the fractional scroll deltas when Blink fully
1470 // supports fractional scrolls. crbug.com/414283.
1471 // TODO(flackr): We should ideally round the fractional scrolls in the same
1472 // direction as the scroll will be snapped but for common cases this is
1473 // equivalent to rounding to the nearest integer offset.
1474 gfx::ScrollOffset current_offset =
1475 scroll_offset->Current(/* is_active_tree */ true);
1476 gfx::ScrollOffset rounded_offset =
1477 gfx::ScrollOffset(roundf(current_offset.x()), roundf(current_offset.y()));
1478 // The calculation of the difference from the rounded active base is to
1479 // represent the integer delta that the main thread should know about.
1480 gfx::ScrollOffset active_base = scroll_offset->ActiveBase();
1481 gfx::ScrollOffset diff_active_base =
1482 gfx::ScrollOffset(active_base.x() - roundf(active_base.x()),
1483 active_base.y() - roundf(active_base.y()));
1484 scroll_offset->SetCurrent(rounded_offset + diff_active_base);
1485 gfx::ScrollOffset delta = scroll_offset->PullDeltaForMainThread();
1486 scroll_offset->SetCurrent(current_offset);
1487 return delta;
1488 }
1489
CollectScrollDeltas(CompositorCommitData * commit_data,ElementId inner_viewport_scroll_element_id,bool use_fractional_deltas,const base::flat_set<ElementId> & snapped_elements)1490 void ScrollTree::CollectScrollDeltas(
1491 CompositorCommitData* commit_data,
1492 ElementId inner_viewport_scroll_element_id,
1493 bool use_fractional_deltas,
1494 const base::flat_set<ElementId>& snapped_elements) {
1495 DCHECK(!property_trees()->is_main_thread);
1496 TRACE_EVENT0("cc", "ScrollTree::CollectScrollDeltas");
1497 for (auto map_entry : synced_scroll_offset_map_) {
1498 gfx::ScrollOffset scroll_delta =
1499 PullDeltaForMainThread(map_entry.second.get(), use_fractional_deltas);
1500
1501 ElementId id = map_entry.first;
1502
1503 base::Optional<TargetSnapAreaElementIds> snap_target_ids;
1504 if (snapped_elements.find(id) != snapped_elements.end()) {
1505 ScrollNode* scroll_node = FindNodeFromElementId(id);
1506 if (scroll_node && scroll_node->snap_container_data) {
1507 snap_target_ids = scroll_node->snap_container_data.value()
1508 .GetTargetSnapAreaElementIds();
1509 }
1510 }
1511
1512 // Snap targets are set at the end of scroll offset animations (i.e when the
1513 // animation state is updated to FINISHED). The state can be updated after
1514 // the compositor's draw stage, which means the next attempt to push the
1515 // snap targets is during the next frame. This makes it possible for the
1516 // scroll delta to be zero.
1517 if (!scroll_delta.IsZero() || snap_target_ids) {
1518 TRACE_EVENT_INSTANT2("cc", "CollectScrollDeltas",
1519 TRACE_EVENT_SCOPE_THREAD, "x", scroll_delta.x(), "y",
1520 scroll_delta.y());
1521 CompositorCommitData::ScrollUpdateInfo update(id, scroll_delta,
1522 snap_target_ids);
1523 if (id == inner_viewport_scroll_element_id) {
1524 // Inner (visual) viewport is stored separately.
1525 commit_data->inner_viewport_scroll = std::move(update);
1526 } else {
1527 commit_data->scrolls.push_back(std::move(update));
1528 }
1529 }
1530 }
1531 }
1532
CollectScrollDeltasForTesting()1533 void ScrollTree::CollectScrollDeltasForTesting() {
1534 LayerTreeSettings settings;
1535 bool use_fractional_deltas = settings.commit_fractional_scroll_deltas;
1536
1537 for (auto map_entry : synced_scroll_offset_map_) {
1538 PullDeltaForMainThread(map_entry.second.get(), use_fractional_deltas);
1539 }
1540 }
1541
PushScrollUpdatesFromMainThread(PropertyTrees * main_property_trees,LayerTreeImpl * sync_tree)1542 void ScrollTree::PushScrollUpdatesFromMainThread(
1543 PropertyTrees* main_property_trees,
1544 LayerTreeImpl* sync_tree) {
1545 DCHECK(!property_trees()->is_main_thread);
1546 const ScrollOffsetMap& main_scroll_offset_map =
1547 main_property_trees->scroll_tree.scroll_offset_map_;
1548
1549 // We first want to clear SyncedProperty instances for layers which were
1550 // destroyed or became non-scrollable on the main thread.
1551 for (auto map_entry = synced_scroll_offset_map_.begin();
1552 map_entry != synced_scroll_offset_map_.end();) {
1553 ElementId id = map_entry->first;
1554 if (main_scroll_offset_map.find(id) == main_scroll_offset_map.end())
1555 map_entry = synced_scroll_offset_map_.erase(map_entry);
1556 else
1557 map_entry++;
1558 }
1559
1560 for (auto map_entry : main_scroll_offset_map) {
1561 ElementId id = map_entry.first;
1562 SyncedScrollOffset* synced_scroll_offset =
1563 GetOrCreateSyncedScrollOffset(id);
1564
1565 // If the value on the main thread differs from the value on the pending
1566 // tree after state sync, we need to update the scroll state on the newly
1567 // committed PropertyTrees.
1568 bool needs_scroll_update =
1569 synced_scroll_offset->PushMainToPending(map_entry.second);
1570
1571 // If we are committing directly to the active tree, push pending to active
1572 // here. If the value differs between the pending and active trees, we need
1573 // to update the scroll state on the newly activated PropertyTrees.
1574 // In the case of pushing to the active tree, even if the pending and active
1575 // tree state match but the value on the active tree changed, we need to
1576 // update the scrollbar geometries.
1577 if (property_trees()->is_active)
1578 needs_scroll_update |= synced_scroll_offset->PushPendingToActive();
1579
1580 if (needs_scroll_update)
1581 sync_tree->DidUpdateScrollOffset(id);
1582 }
1583 }
1584
PushScrollUpdatesFromPendingTree(PropertyTrees * pending_property_trees,LayerTreeImpl * active_tree)1585 void ScrollTree::PushScrollUpdatesFromPendingTree(
1586 PropertyTrees* pending_property_trees,
1587 LayerTreeImpl* active_tree) {
1588 DCHECK(property_trees()->is_active);
1589 DCHECK(!pending_property_trees->is_main_thread);
1590 DCHECK(!pending_property_trees->is_active);
1591
1592 // When pushing to the active tree, we can simply copy over the map from the
1593 // pending tree. The pending and active tree hold a reference to the same
1594 // SyncedProperty instances.
1595 synced_scroll_offset_map_.clear();
1596 for (auto map_entry :
1597 pending_property_trees->scroll_tree.synced_scroll_offset_map_) {
1598 synced_scroll_offset_map_[map_entry.first] = map_entry.second;
1599 if (map_entry.second->PushPendingToActive())
1600 active_tree->DidUpdateScrollOffset(map_entry.first);
1601 }
1602 }
1603
ApplySentScrollDeltasFromAbortedCommit()1604 void ScrollTree::ApplySentScrollDeltasFromAbortedCommit() {
1605 DCHECK(property_trees()->is_active);
1606 for (auto& map_entry : synced_scroll_offset_map_)
1607 map_entry.second->AbortCommit();
1608 }
1609
SetBaseScrollOffset(ElementId id,const gfx::ScrollOffset & scroll_offset)1610 void ScrollTree::SetBaseScrollOffset(ElementId id,
1611 const gfx::ScrollOffset& scroll_offset) {
1612 if (property_trees()->is_main_thread) {
1613 scroll_offset_map_[id] = scroll_offset;
1614 return;
1615 }
1616
1617 // Scroll offset updates on the impl thread should only be for layers which
1618 // were created on the main thread. But this method is called when we build
1619 // PropertyTrees on the impl thread from LayerTreeImpl.
1620 GetOrCreateSyncedScrollOffset(id)->PushMainToPending(scroll_offset);
1621 }
1622
SetScrollOffset(ElementId id,const gfx::ScrollOffset & scroll_offset)1623 bool ScrollTree::SetScrollOffset(ElementId id,
1624 const gfx::ScrollOffset& scroll_offset) {
1625 // TODO(crbug.com/1087088): Remove TRACE_EVENT call when the bug is fixed
1626 TRACE_EVENT2("cc", "ScrollTree::SetScrollOffset", "x", scroll_offset.x(), "y",
1627 scroll_offset.y());
1628 if (property_trees()->is_main_thread) {
1629 if (scroll_offset_map_[id] == scroll_offset)
1630 return false;
1631 scroll_offset_map_[id] = scroll_offset;
1632 return true;
1633 }
1634
1635 if (property_trees()->is_active) {
1636 return GetOrCreateSyncedScrollOffset(id)->SetCurrent(scroll_offset);
1637 }
1638
1639 return false;
1640 }
1641
UpdateScrollOffsetBaseForTesting(ElementId id,const gfx::ScrollOffset & offset)1642 bool ScrollTree::UpdateScrollOffsetBaseForTesting(
1643 ElementId id,
1644 const gfx::ScrollOffset& offset) {
1645 DCHECK(!property_trees()->is_main_thread);
1646 SyncedScrollOffset* synced_scroll_offset = GetOrCreateSyncedScrollOffset(id);
1647 bool changed = synced_scroll_offset->PushMainToPending(offset);
1648 if (property_trees()->is_active)
1649 changed |= synced_scroll_offset->PushPendingToActive();
1650 return changed;
1651 }
1652
SetScrollOffsetDeltaForTesting(ElementId id,const gfx::Vector2dF & delta)1653 bool ScrollTree::SetScrollOffsetDeltaForTesting(ElementId id,
1654 const gfx::Vector2dF& delta) {
1655 return GetOrCreateSyncedScrollOffset(id)->SetCurrent(
1656 GetOrCreateSyncedScrollOffset(id)->ActiveBase() +
1657 gfx::ScrollOffset(delta));
1658 }
1659
GetScrollOffsetBaseForTesting(ElementId id) const1660 const gfx::ScrollOffset ScrollTree::GetScrollOffsetBaseForTesting(
1661 ElementId id) const {
1662 DCHECK(!property_trees()->is_main_thread);
1663 if (GetSyncedScrollOffset(id))
1664 return property_trees()->is_active
1665 ? GetSyncedScrollOffset(id)->ActiveBase()
1666 : GetSyncedScrollOffset(id)->PendingBase();
1667 else
1668 return gfx::ScrollOffset();
1669 }
1670
GetScrollOffsetDeltaForTesting(ElementId id) const1671 const gfx::ScrollOffset ScrollTree::GetScrollOffsetDeltaForTesting(
1672 ElementId id) const {
1673 DCHECK(!property_trees()->is_main_thread);
1674 if (GetSyncedScrollOffset(id))
1675 return property_trees()->is_active
1676 ? GetSyncedScrollOffset(id)->Delta()
1677 : GetSyncedScrollOffset(id)->PendingDelta().get();
1678 else
1679 return gfx::ScrollOffset();
1680 }
1681
ScrollBy(const ScrollNode & scroll_node,const gfx::Vector2dF & scroll,LayerTreeImpl * layer_tree_impl)1682 gfx::Vector2dF ScrollTree::ScrollBy(const ScrollNode& scroll_node,
1683 const gfx::Vector2dF& scroll,
1684 LayerTreeImpl* layer_tree_impl) {
1685 gfx::ScrollOffset adjusted_scroll(scroll);
1686 if (!scroll_node.user_scrollable_horizontal)
1687 adjusted_scroll.set_x(0);
1688 if (!scroll_node.user_scrollable_vertical)
1689 adjusted_scroll.set_y(0);
1690 DCHECK(scroll_node.scrollable);
1691 gfx::ScrollOffset old_offset = current_scroll_offset(scroll_node.element_id);
1692 gfx::ScrollOffset new_offset =
1693 ClampScrollOffsetToLimits(old_offset + adjusted_scroll, scroll_node);
1694 if (SetScrollOffset(scroll_node.element_id, new_offset))
1695 layer_tree_impl->DidUpdateScrollOffset(scroll_node.element_id);
1696
1697 gfx::ScrollOffset unscrolled =
1698 old_offset + gfx::ScrollOffset(scroll) - new_offset;
1699 return gfx::Vector2dF(unscrolled.x(), unscrolled.y());
1700 }
1701
ClampScrollOffsetToLimits(gfx::ScrollOffset offset,const ScrollNode & scroll_node) const1702 gfx::ScrollOffset ScrollTree::ClampScrollOffsetToLimits(
1703 gfx::ScrollOffset offset,
1704 const ScrollNode& scroll_node) const {
1705 offset.SetToMin(MaxScrollOffset(scroll_node.id));
1706 offset.SetToMax(gfx::ScrollOffset());
1707 return offset;
1708 }
1709
SetScrollCallbacks(base::WeakPtr<ScrollCallbacks> callbacks)1710 void ScrollTree::SetScrollCallbacks(base::WeakPtr<ScrollCallbacks> callbacks) {
1711 DCHECK(property_trees()->is_main_thread);
1712 callbacks_ = std::move(callbacks);
1713 }
1714
NotifyDidScroll(ElementId scroll_element_id,const gfx::ScrollOffset & scroll_offset,const base::Optional<TargetSnapAreaElementIds> & snap_target_ids)1715 void ScrollTree::NotifyDidScroll(
1716 ElementId scroll_element_id,
1717 const gfx::ScrollOffset& scroll_offset,
1718 const base::Optional<TargetSnapAreaElementIds>& snap_target_ids) {
1719 DCHECK(property_trees()->is_main_thread);
1720 if (callbacks_)
1721 callbacks_->DidScroll(scroll_element_id, scroll_offset, snap_target_ids);
1722 }
1723
NotifyDidChangeScrollbarsHidden(ElementId scroll_element_id,bool hidden)1724 void ScrollTree::NotifyDidChangeScrollbarsHidden(ElementId scroll_element_id,
1725 bool hidden) {
1726 DCHECK(property_trees()->is_main_thread);
1727 if (callbacks_)
1728 callbacks_->DidChangeScrollbarsHidden(scroll_element_id, hidden);
1729 }
1730
PropertyTreesCachedData()1731 PropertyTreesCachedData::PropertyTreesCachedData()
1732 : transform_tree_update_number(0) {
1733 animation_scales.clear();
1734 }
1735
1736 PropertyTreesCachedData::~PropertyTreesCachedData() = default;
1737
PropertyTrees()1738 PropertyTrees::PropertyTrees()
1739 : needs_rebuild(true),
1740 changed(false),
1741 full_tree_damaged(false),
1742 sequence_number(0),
1743 is_main_thread(true),
1744 is_active(false) {
1745 transform_tree.SetPropertyTrees(this);
1746 effect_tree.SetPropertyTrees(this);
1747 clip_tree.SetPropertyTrees(this);
1748 scroll_tree.SetPropertyTrees(this);
1749 }
1750
1751 PropertyTrees::~PropertyTrees() = default;
1752
1753 #if DCHECK_IS_ON()
operator ==(const PropertyTrees & other) const1754 bool PropertyTrees::operator==(const PropertyTrees& other) const {
1755 return transform_tree == other.transform_tree &&
1756 effect_tree == other.effect_tree && clip_tree == other.clip_tree &&
1757 scroll_tree == other.scroll_tree &&
1758 element_id_to_effect_node_index ==
1759 other.element_id_to_effect_node_index &&
1760 element_id_to_scroll_node_index ==
1761 other.element_id_to_scroll_node_index &&
1762 element_id_to_transform_node_index ==
1763 other.element_id_to_transform_node_index &&
1764 needs_rebuild == other.needs_rebuild && changed == other.changed &&
1765 full_tree_damaged == other.full_tree_damaged &&
1766 is_main_thread == other.is_main_thread &&
1767 is_active == other.is_active &&
1768 sequence_number == other.sequence_number;
1769 }
1770 #endif
1771
operator =(const PropertyTrees & from)1772 PropertyTrees& PropertyTrees::operator=(const PropertyTrees& from) {
1773 transform_tree = from.transform_tree;
1774 effect_tree = from.effect_tree;
1775 clip_tree = from.clip_tree;
1776 scroll_tree = from.scroll_tree;
1777 element_id_to_effect_node_index = from.element_id_to_effect_node_index;
1778 element_id_to_scroll_node_index = from.element_id_to_scroll_node_index;
1779 element_id_to_transform_node_index = from.element_id_to_transform_node_index;
1780 needs_rebuild = from.needs_rebuild;
1781 changed = from.changed;
1782 full_tree_damaged = from.full_tree_damaged;
1783 sequence_number = from.sequence_number;
1784 is_main_thread = from.is_main_thread;
1785 is_active = from.is_active;
1786 inner_viewport_container_bounds_delta_ =
1787 from.inner_viewport_container_bounds_delta();
1788 outer_viewport_container_bounds_delta_ =
1789 from.outer_viewport_container_bounds_delta();
1790 transform_tree.SetPropertyTrees(this);
1791 effect_tree.SetPropertyTrees(this);
1792 clip_tree.SetPropertyTrees(this);
1793 scroll_tree.SetPropertyTrees(this);
1794 ResetCachedData();
1795 return *this;
1796 }
1797
clear()1798 void PropertyTrees::clear() {
1799 transform_tree.clear();
1800 clip_tree.clear();
1801 effect_tree.clear();
1802 scroll_tree.clear();
1803 element_id_to_effect_node_index.clear();
1804 element_id_to_scroll_node_index.clear();
1805 element_id_to_transform_node_index.clear();
1806
1807 needs_rebuild = true;
1808 full_tree_damaged = false;
1809 changed = false;
1810 sequence_number++;
1811
1812 #if DCHECK_IS_ON()
1813 PropertyTrees tree;
1814 tree.transform_tree = transform_tree;
1815 tree.effect_tree = effect_tree;
1816 tree.clip_tree = clip_tree;
1817 tree.scroll_tree = scroll_tree;
1818 tree.scroll_tree.CopyCompleteTreeState(scroll_tree);
1819
1820 tree.sequence_number = sequence_number;
1821 tree.is_main_thread = is_main_thread;
1822 tree.is_active = is_active;
1823 DCHECK(tree == *this);
1824 #endif
1825 }
1826
SetInnerViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta)1827 void PropertyTrees::SetInnerViewportContainerBoundsDelta(
1828 gfx::Vector2dF bounds_delta) {
1829 if (inner_viewport_container_bounds_delta_ == bounds_delta)
1830 return;
1831
1832 inner_viewport_container_bounds_delta_ = bounds_delta;
1833 }
1834
SetOuterViewportContainerBoundsDelta(gfx::Vector2dF bounds_delta)1835 void PropertyTrees::SetOuterViewportContainerBoundsDelta(
1836 gfx::Vector2dF bounds_delta) {
1837 if (outer_viewport_container_bounds_delta_ == bounds_delta)
1838 return;
1839
1840 outer_viewport_container_bounds_delta_ = bounds_delta;
1841 transform_tree.UpdateOuterViewportContainerBoundsDelta();
1842 }
1843
ElementIsAnimatingChanged(const PropertyToElementIdMap & element_id_map,const PropertyAnimationState & mask,const PropertyAnimationState & state,bool check_node_existence)1844 bool PropertyTrees::ElementIsAnimatingChanged(
1845 const PropertyToElementIdMap& element_id_map,
1846 const PropertyAnimationState& mask,
1847 const PropertyAnimationState& state,
1848 bool check_node_existence) {
1849 bool updated_transform = false;
1850 for (int property = TargetProperty::FIRST_TARGET_PROPERTY;
1851 property <= TargetProperty::LAST_TARGET_PROPERTY; ++property) {
1852 if (!mask.currently_running[property] &&
1853 !mask.potentially_animating[property])
1854 continue;
1855
1856 // The mask represents which properties have had their state changed. This
1857 // can include properties for which there are no longer any animations, in
1858 // which case there will not be an entry in the map.
1859 //
1860 // It is unclear whether this is desirable; it may be that we are missing
1861 // updates to property nodes here because we no longer have the required
1862 // ElementId to look them up. See http://crbug.com/912574 for context around
1863 // why this code was rewritten.
1864 auto it = element_id_map.find(static_cast<TargetProperty::Type>(property));
1865 if (it == element_id_map.end())
1866 continue;
1867
1868 const ElementId element_id = it->second;
1869 switch (property) {
1870 case TargetProperty::TRANSFORM:
1871 if (TransformNode* transform_node =
1872 transform_tree.FindNodeFromElementId(element_id)) {
1873 if (mask.currently_running[property])
1874 transform_node->is_currently_animating =
1875 state.currently_running[property];
1876 if (mask.potentially_animating[property]) {
1877 transform_node->has_potential_animation =
1878 state.potentially_animating[property];
1879 transform_tree.set_needs_update(true);
1880 // We track transform updates specifically, whereas we
1881 // don't do so for opacity/filter, because whether a
1882 // transform is animating can change what layer(s) we
1883 // draw.
1884 updated_transform = true;
1885 }
1886 } else {
1887 DCHECK_NODE_EXISTENCE(check_node_existence, state, property,
1888 needs_rebuild)
1889 << "Attempting to animate non existent transform node";
1890 }
1891 break;
1892 case TargetProperty::OPACITY:
1893 if (EffectNode* effect_node =
1894 effect_tree.FindNodeFromElementId(element_id)) {
1895 if (mask.currently_running[property])
1896 effect_node->is_currently_animating_opacity =
1897 state.currently_running[property];
1898 if (mask.potentially_animating[property]) {
1899 effect_node->has_potential_opacity_animation =
1900 state.potentially_animating[property];
1901 // We may need to propagate things like screen space opacity.
1902 effect_tree.set_needs_update(true);
1903 }
1904 } else {
1905 DCHECK_NODE_EXISTENCE(check_node_existence, state, property,
1906 needs_rebuild)
1907 << "Attempting to animate opacity on non existent effect node";
1908 }
1909 break;
1910 case TargetProperty::FILTER:
1911 if (EffectNode* effect_node =
1912 effect_tree.FindNodeFromElementId(element_id)) {
1913 if (mask.currently_running[property])
1914 effect_node->is_currently_animating_filter =
1915 state.currently_running[property];
1916 if (mask.potentially_animating[property])
1917 effect_node->has_potential_filter_animation =
1918 state.potentially_animating[property];
1919 // Filter animation changes only the node, and the subtree does not
1920 // care, thus there is no need to request property tree update.
1921 } else {
1922 DCHECK_NODE_EXISTENCE(check_node_existence, state, property,
1923 needs_rebuild)
1924 << "Attempting to animate filter on non existent effect node";
1925 }
1926 break;
1927 case TargetProperty::BACKDROP_FILTER:
1928 if (EffectNode* effect_node =
1929 effect_tree.FindNodeFromElementId(element_id)) {
1930 if (mask.currently_running[property])
1931 effect_node->is_currently_animating_backdrop_filter =
1932 state.currently_running[property];
1933 if (mask.potentially_animating[property])
1934 effect_node->has_potential_backdrop_filter_animation =
1935 state.potentially_animating[property];
1936 // Backdrop-filter animation changes only the node, and the subtree
1937 // does not care, thus there is no need to request property tree
1938 // update.
1939 } else {
1940 DCHECK_NODE_EXISTENCE(check_node_existence, state, property,
1941 needs_rebuild)
1942 << "Attempting to animate filter on non existent effect node";
1943 }
1944 break;
1945 default:
1946 break;
1947 }
1948 }
1949 return updated_transform;
1950 }
1951
AnimationScalesChanged(ElementId element_id,float maximum_scale,float starting_scale)1952 void PropertyTrees::AnimationScalesChanged(ElementId element_id,
1953 float maximum_scale,
1954 float starting_scale) {
1955 if (TransformNode* transform_node =
1956 transform_tree.FindNodeFromElementId(element_id)) {
1957 transform_node->maximum_animation_scale = maximum_scale;
1958 transform_node->starting_animation_scale = starting_scale;
1959 UpdateTransformTreeUpdateNumber();
1960 }
1961 }
1962
UpdateChangeTracking()1963 void PropertyTrees::UpdateChangeTracking() {
1964 for (int id = EffectTree::kContentsRootNodeId;
1965 id < static_cast<int>(effect_tree.size()); ++id) {
1966 EffectNode* node = effect_tree.Node(id);
1967 EffectNode* parent_node = effect_tree.parent(node);
1968 effect_tree.UpdateEffectChanged(node, parent_node);
1969 }
1970 for (int i = TransformTree::kContentsRootNodeId;
1971 i < static_cast<int>(transform_tree.size()); ++i) {
1972 TransformNode* node = transform_tree.Node(i);
1973 TransformNode* parent_node = transform_tree.parent(node);
1974 transform_tree.UpdateTransformChanged(node, parent_node);
1975 }
1976 }
1977
PushChangeTrackingTo(PropertyTrees * tree)1978 void PropertyTrees::PushChangeTrackingTo(PropertyTrees* tree) {
1979 for (int id = EffectTree::kContentsRootNodeId;
1980 id < static_cast<int>(effect_tree.size()); ++id) {
1981 EffectNode* node = effect_tree.Node(id);
1982 if (node->effect_changed) {
1983 EffectNode* target_node = tree->effect_tree.Node(node->id);
1984 target_node->effect_changed = true;
1985 }
1986 }
1987 for (int id = TransformTree::kContentsRootNodeId;
1988 id < static_cast<int>(transform_tree.size()); ++id) {
1989 TransformNode* node = transform_tree.Node(id);
1990 if (node->transform_changed) {
1991 TransformNode* target_node = tree->transform_tree.Node(node->id);
1992 target_node->transform_changed = true;
1993 }
1994 }
1995 // Ensure that change tracking is updated even if property trees don't have
1996 // other reasons to get updated.
1997 tree->UpdateChangeTracking();
1998 tree->full_tree_damaged = full_tree_damaged;
1999 }
2000
ResetAllChangeTracking()2001 void PropertyTrees::ResetAllChangeTracking() {
2002 transform_tree.ResetChangeTracking();
2003 effect_tree.ResetChangeTracking();
2004 changed = false;
2005 full_tree_damaged = false;
2006 }
2007
AsTracedValue() const2008 std::unique_ptr<base::trace_event::TracedValue> PropertyTrees::AsTracedValue()
2009 const {
2010 auto value = base::WrapUnique(new base::trace_event::TracedValue);
2011 AsValueInto(value.get());
2012 return value;
2013 }
2014
AsValueInto(base::trace_event::TracedValue * value) const2015 void PropertyTrees::AsValueInto(base::trace_event::TracedValue* value) const {
2016 value->SetInteger("sequence_number", sequence_number);
2017
2018 value->BeginDictionary("transform_tree");
2019 transform_tree.AsValueInto(value);
2020 value->EndDictionary();
2021
2022 value->BeginDictionary("effect_tree");
2023 effect_tree.AsValueInto(value);
2024 value->EndDictionary();
2025
2026 value->BeginDictionary("clip_tree");
2027 clip_tree.AsValueInto(value);
2028 value->EndDictionary();
2029
2030 value->BeginDictionary("scroll_tree");
2031 scroll_tree.AsValueInto(value);
2032 value->EndDictionary();
2033 }
2034
ToString() const2035 std::string PropertyTrees::ToString() const {
2036 base::trace_event::TracedValueJSON value;
2037 AsValueInto(&value);
2038 return value.ToFormattedJSON();
2039 }
2040
GetAnimationScales(int transform_node_id,LayerTreeImpl * layer_tree_impl)2041 CombinedAnimationScale PropertyTrees::GetAnimationScales(
2042 int transform_node_id,
2043 LayerTreeImpl* layer_tree_impl) {
2044 AnimationScaleData* animation_scales =
2045 &cached_data_.animation_scales[transform_node_id];
2046 if (animation_scales->update_number !=
2047 cached_data_.transform_tree_update_number) {
2048 TransformNode* node = transform_tree.Node(transform_node_id);
2049 TransformNode* parent_node = transform_tree.parent(node);
2050 bool ancestor_is_animating_scale = false;
2051 float ancestor_maximum_target_scale = kNotScaled;
2052 float ancestor_starting_animation_scale = kNotScaled;
2053 if (parent_node) {
2054 CombinedAnimationScale combined_animation_scale =
2055 GetAnimationScales(parent_node->id, layer_tree_impl);
2056 ancestor_maximum_target_scale =
2057 combined_animation_scale.maximum_animation_scale;
2058 ancestor_starting_animation_scale =
2059 combined_animation_scale.starting_animation_scale;
2060 ancestor_is_animating_scale =
2061 cached_data_.animation_scales[parent_node->id]
2062 .to_screen_has_scale_animation;
2063 }
2064
2065 bool node_is_animating_scale =
2066 node->maximum_animation_scale != kNotScaled &&
2067 node->starting_animation_scale != kNotScaled;
2068
2069 animation_scales->to_screen_has_scale_animation =
2070 node_is_animating_scale || ancestor_is_animating_scale;
2071
2072 // Once we've failed to compute a maximum animated scale at an ancestor, we
2073 // continue to fail.
2074 bool failed_at_ancestor = ancestor_is_animating_scale &&
2075 ancestor_maximum_target_scale == kNotScaled;
2076
2077 // Computing maximum animated scale in the presence of non-scale/translation
2078 // transforms isn't supported.
2079 bool failed_for_non_scale_or_translation =
2080 !node->to_parent.IsScaleOrTranslation();
2081
2082 // We don't attempt to accumulate animation scale from multiple nodes with
2083 // scale animations, because of the risk of significant overestimation. For
2084 // example, one node might be increasing scale from 1 to 10 at the same time
2085 // as another node is decreasing scale from 10 to 1. Naively combining these
2086 // scales would produce a scale of 100.
2087 bool failed_for_multiple_scale_animations =
2088 ancestor_is_animating_scale && node_is_animating_scale;
2089
2090 if (failed_at_ancestor || failed_for_non_scale_or_translation ||
2091 failed_for_multiple_scale_animations) {
2092 // This ensures that descendants know we've failed to compute a maximum
2093 // animated scale.
2094 animation_scales->to_screen_has_scale_animation = true;
2095 animation_scales->combined_maximum_animation_target_scale = kNotScaled;
2096 animation_scales->combined_starting_animation_scale = kNotScaled;
2097 } else if (!animation_scales->to_screen_has_scale_animation) {
2098 animation_scales->combined_maximum_animation_target_scale = kNotScaled;
2099 animation_scales->combined_starting_animation_scale = kNotScaled;
2100 } else if (!node_is_animating_scale) {
2101 // An ancestor is animating scale.
2102 gfx::Vector2dF local_scales =
2103 MathUtil::ComputeTransform2dScaleComponents(node->local, kNotScaled);
2104 float max_local_scale = std::max(local_scales.x(), local_scales.y());
2105 animation_scales->combined_maximum_animation_target_scale =
2106 max_local_scale * ancestor_maximum_target_scale;
2107 animation_scales->combined_starting_animation_scale =
2108 max_local_scale * ancestor_starting_animation_scale;
2109 } else {
2110 gfx::Vector2dF ancestor_scales =
2111 parent_node
2112 ? MathUtil::ComputeTransform2dScaleComponents(
2113 transform_tree.ToScreen(parent_node->id), kNotScaled)
2114 : gfx::Vector2dF(1.f, 1.f);
2115
2116 float max_ancestor_scale =
2117 std::max(ancestor_scales.x(), ancestor_scales.y());
2118 animation_scales->combined_maximum_animation_target_scale =
2119 max_ancestor_scale * node->maximum_animation_scale;
2120 animation_scales->combined_starting_animation_scale =
2121 max_ancestor_scale * node->starting_animation_scale;
2122 }
2123 animation_scales->update_number = cached_data_.transform_tree_update_number;
2124 }
2125 return CombinedAnimationScale(
2126 animation_scales->combined_maximum_animation_target_scale,
2127 animation_scales->combined_starting_animation_scale);
2128 }
2129
SetAnimationScalesForTesting(int transform_id,float maximum_animation_scale,float starting_animation_scale)2130 void PropertyTrees::SetAnimationScalesForTesting(
2131 int transform_id,
2132 float maximum_animation_scale,
2133 float starting_animation_scale) {
2134 cached_data_.animation_scales[transform_id]
2135 .combined_maximum_animation_target_scale = maximum_animation_scale;
2136 cached_data_.animation_scales[transform_id]
2137 .combined_starting_animation_scale = starting_animation_scale;
2138 cached_data_.animation_scales[transform_id].update_number =
2139 cached_data_.transform_tree_update_number;
2140 }
2141
GetToTarget(int transform_id,int effect_id,gfx::Transform * to_target) const2142 bool PropertyTrees::GetToTarget(int transform_id,
2143 int effect_id,
2144 gfx::Transform* to_target) const {
2145 if (effect_id == EffectTree::kContentsRootNodeId) {
2146 *to_target = transform_tree.ToScreen(transform_id);
2147 return true;
2148 }
2149 DrawTransforms& transforms = GetDrawTransforms(transform_id, effect_id);
2150 if (transforms.to_valid) {
2151 *to_target = transforms.to_target;
2152 return true;
2153 } else if (!transforms.might_be_invertible) {
2154 return false;
2155 } else {
2156 transforms.might_be_invertible =
2157 transforms.from_target.GetInverse(to_target);
2158 transforms.to_valid = transforms.might_be_invertible;
2159 transforms.to_target = *to_target;
2160 return transforms.to_valid;
2161 }
2162 }
2163
GetFromTarget(int transform_id,int effect_id,gfx::Transform * from_target) const2164 bool PropertyTrees::GetFromTarget(int transform_id,
2165 int effect_id,
2166 gfx::Transform* from_target) const {
2167 const TransformNode* node = transform_tree.Node(transform_id);
2168 if (node->ancestors_are_invertible &&
2169 effect_id == EffectTree::kContentsRootNodeId) {
2170 *from_target = transform_tree.FromScreen(transform_id);
2171 return true;
2172 }
2173 DrawTransforms& transforms = GetDrawTransforms(transform_id, effect_id);
2174 if (transforms.from_valid) {
2175 *from_target = transforms.from_target;
2176 return true;
2177 } else if (!transforms.might_be_invertible) {
2178 return false;
2179 } else {
2180 transforms.might_be_invertible =
2181 transforms.to_target.GetInverse(from_target);
2182 transforms.from_valid = transforms.might_be_invertible;
2183 transforms.from_target = *from_target;
2184 return transforms.from_valid;
2185 }
2186 }
2187
FetchDrawTransformsDataFromCache(int transform_id,int dest_id) const2188 DrawTransformData& PropertyTrees::FetchDrawTransformsDataFromCache(
2189 int transform_id,
2190 int dest_id) const {
2191 for (auto& transform_data : cached_data_.draw_transforms[transform_id]) {
2192 // We initialize draw_transforms with 1 element vectors when
2193 // ResetCachedData, so if we hit an invalid target id, it means it's the
2194 // first time we compute draw transforms after reset.
2195 if (transform_data.target_id == dest_id ||
2196 transform_data.target_id == EffectTree::kInvalidNodeId) {
2197 return transform_data;
2198 }
2199 }
2200 // Add an entry to the cache.
2201 cached_data_.draw_transforms[transform_id].push_back(DrawTransformData());
2202 DrawTransformData& data = cached_data_.draw_transforms[transform_id].back();
2203 data.update_number = -1;
2204 data.target_id = dest_id;
2205 return data;
2206 }
2207
FetchClipRectFromCache(int clip_id,int target_id)2208 ClipRectData* PropertyTrees::FetchClipRectFromCache(int clip_id,
2209 int target_id) {
2210 ClipNode* clip_node = clip_tree.Node(clip_id);
2211 for (size_t i = 0; i < clip_node->cached_clip_rects->size(); ++i) {
2212 auto& data = clip_node->cached_clip_rects[i];
2213 if (data.target_id == target_id || data.target_id == -1)
2214 return &data;
2215 }
2216 clip_node->cached_clip_rects->emplace_back();
2217 return &clip_node->cached_clip_rects->back();
2218 }
2219
HasElement(ElementId element_id) const2220 bool PropertyTrees::HasElement(ElementId element_id) const {
2221 if (!element_id)
2222 return false;
2223 return element_id_to_effect_node_index.contains(element_id) ||
2224 element_id_to_scroll_node_index.contains(element_id) ||
2225 element_id_to_transform_node_index.contains(element_id);
2226 }
2227
GetDrawTransforms(int transform_id,int effect_id) const2228 DrawTransforms& PropertyTrees::GetDrawTransforms(int transform_id,
2229 int effect_id) const {
2230 const EffectNode* effect_node = effect_tree.Node(effect_id);
2231 int dest_id = effect_node->transform_id;
2232
2233 DrawTransformData& data =
2234 FetchDrawTransformsDataFromCache(transform_id, dest_id);
2235
2236 DCHECK(data.update_number != cached_data_.transform_tree_update_number ||
2237 data.target_id != EffectTree::kInvalidNodeId);
2238 if (data.update_number == cached_data_.transform_tree_update_number)
2239 return data.transforms;
2240
2241 // Cache miss.
2242 gfx::Transform target_space_transform;
2243 gfx::Transform from_target;
2244 bool already_computed_inverse = false;
2245 if (transform_id == dest_id) {
2246 target_space_transform.Scale(effect_node->surface_contents_scale.x(),
2247 effect_node->surface_contents_scale.y());
2248 data.transforms.to_valid = true;
2249 data.transforms.from_valid = false;
2250 } else if (transform_id > dest_id) {
2251 transform_tree.CombineTransformsBetween(transform_id, dest_id,
2252 &target_space_transform);
2253 target_space_transform.matrix().postScale(
2254 effect_node->surface_contents_scale.x(),
2255 effect_node->surface_contents_scale.y(), 1.f);
2256 data.transforms.to_valid = true;
2257 data.transforms.from_valid = false;
2258 data.transforms.might_be_invertible = true;
2259 } else {
2260 gfx::Transform combined_transform;
2261 transform_tree.CombineTransformsBetween(dest_id, transform_id,
2262 &combined_transform);
2263 if (effect_node->surface_contents_scale.x() != 0.f &&
2264 effect_node->surface_contents_scale.y() != 0.f)
2265 combined_transform.Scale(1.0f / effect_node->surface_contents_scale.x(),
2266 1.0f / effect_node->surface_contents_scale.y());
2267 bool invertible = combined_transform.GetInverse(&target_space_transform);
2268 data.transforms.might_be_invertible = invertible;
2269 data.transforms.to_valid = invertible;
2270 data.transforms.from_valid = true;
2271 from_target = combined_transform;
2272 already_computed_inverse = true;
2273 }
2274
2275 if (!already_computed_inverse)
2276 data.transforms.to_valid = true;
2277 data.update_number = cached_data_.transform_tree_update_number;
2278 data.target_id = dest_id;
2279 data.transforms.from_target = from_target;
2280 data.transforms.to_target = target_space_transform;
2281 return data.transforms;
2282 }
2283
ResetCachedData()2284 void PropertyTrees::ResetCachedData() {
2285 cached_data_.transform_tree_update_number = 0;
2286 const auto transform_count = transform_tree.nodes().size();
2287 cached_data_.animation_scales.resize(transform_count);
2288 for (auto& animation_scale : cached_data_.animation_scales)
2289 animation_scale.update_number = -1;
2290
2291 cached_data_.draw_transforms.resize(transform_count,
2292 std::vector<DrawTransformData>(1));
2293 for (auto& draw_transforms_for_id : cached_data_.draw_transforms) {
2294 draw_transforms_for_id.resize(1);
2295 draw_transforms_for_id[0].update_number = -1;
2296 draw_transforms_for_id[0].target_id = EffectTree::kInvalidNodeId;
2297 }
2298 }
2299
UpdateTransformTreeUpdateNumber()2300 void PropertyTrees::UpdateTransformTreeUpdateNumber() {
2301 cached_data_.transform_tree_update_number++;
2302 }
2303
ToScreenSpaceTransformWithoutSurfaceContentsScale(int transform_id,int effect_id) const2304 gfx::Transform PropertyTrees::ToScreenSpaceTransformWithoutSurfaceContentsScale(
2305 int transform_id,
2306 int effect_id) const {
2307 if (transform_id == TransformTree::kRootNodeId) {
2308 return gfx::Transform();
2309 }
2310 gfx::Transform screen_space_transform = transform_tree.ToScreen(transform_id);
2311 const EffectNode* effect_node = effect_tree.Node(effect_id);
2312
2313 if (effect_node->surface_contents_scale.x() != 0.0 &&
2314 effect_node->surface_contents_scale.y() != 0.0)
2315 screen_space_transform.Scale(1.0 / effect_node->surface_contents_scale.x(),
2316 1.0 / effect_node->surface_contents_scale.y());
2317 return screen_space_transform;
2318 }
2319
2320 } // namespace cc
2321