This sets up API to release OutputSurface from LTHClient.
[chromium-blink-merge.git] / cc / trees / property_tree.cc
blob049e387e22aafa4d3c9d75dfc6a33851d12cce15
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.
5 #include <set>
6 #include <vector>
8 #include "base/logging.h"
9 #include "cc/base/math_util.h"
10 #include "cc/trees/property_tree.h"
12 namespace cc {
14 template <typename T>
15 PropertyTree<T>::PropertyTree()
16 : needs_update_(false) {
17 nodes_.push_back(T());
18 back()->id = 0;
19 back()->parent_id = -1;
22 template <typename T>
23 PropertyTree<T>::~PropertyTree() {
26 TransformTree::TransformTree() : source_to_parent_updates_allowed_(true) {}
28 TransformTree::~TransformTree() {
31 template <typename T>
32 int PropertyTree<T>::Insert(const T& tree_node, int parent_id) {
33 DCHECK_GT(nodes_.size(), 0u);
34 nodes_.push_back(tree_node);
35 T& node = nodes_.back();
36 node.parent_id = parent_id;
37 node.id = static_cast<int>(nodes_.size()) - 1;
38 return node.id;
41 template <typename T>
42 void PropertyTree<T>::clear() {
43 nodes_.clear();
44 nodes_.push_back(T());
45 back()->id = 0;
46 back()->parent_id = -1;
49 template class PropertyTree<TransformNode>;
50 template class PropertyTree<ClipNode>;
51 template class PropertyTree<EffectNode>;
53 TransformNodeData::TransformNodeData()
54 : target_id(-1),
55 content_target_id(-1),
56 source_node_id(-1),
57 needs_local_transform_update(true),
58 is_invertible(true),
59 ancestors_are_invertible(true),
60 is_animated(false),
61 to_screen_is_animated(false),
62 has_only_translation_animations(true),
63 to_screen_has_scale_animation(false),
64 flattens_inherited_transform(false),
65 node_and_ancestors_are_flat(true),
66 node_and_ancestors_have_only_integer_translation(true),
67 scrolls(false),
68 needs_sublayer_scale(false),
69 affected_by_inner_viewport_bounds_delta_x(false),
70 affected_by_inner_viewport_bounds_delta_y(false),
71 affected_by_outer_viewport_bounds_delta_x(false),
72 affected_by_outer_viewport_bounds_delta_y(false),
73 layer_scale_factor(1.0f),
74 post_local_scale_factor(1.0f),
75 local_maximum_animation_target_scale(0.f),
76 local_starting_animation_scale(0.f),
77 combined_maximum_animation_target_scale(0.f),
78 combined_starting_animation_scale(0.f) {}
80 TransformNodeData::~TransformNodeData() {
83 void TransformNodeData::update_pre_local_transform(
84 const gfx::Point3F& transform_origin) {
85 pre_local.MakeIdentity();
86 pre_local.Translate3d(-transform_origin.x(), -transform_origin.y(),
87 -transform_origin.z());
90 void TransformNodeData::update_post_local_transform(
91 const gfx::PointF& position,
92 const gfx::Point3F& transform_origin) {
93 post_local.MakeIdentity();
94 post_local.Scale(post_local_scale_factor, post_local_scale_factor);
95 post_local.Translate3d(
96 position.x() + source_offset.x() + transform_origin.x(),
97 position.y() + source_offset.y() + transform_origin.y(),
98 transform_origin.z());
101 ClipNodeData::ClipNodeData()
102 : transform_id(-1),
103 target_id(-1),
104 inherit_parent_target_space_clip(false),
105 requires_tight_clip_rect(true),
106 render_surface_is_clipped(false) {}
108 EffectNodeData::EffectNodeData()
109 : opacity(1.f),
110 screen_space_opacity(1.f),
111 has_render_surface(false),
112 transform_id(0),
113 clip_id(0) {}
115 void TransformTree::clear() {
116 PropertyTree<TransformNode>::clear();
118 nodes_affected_by_inner_viewport_bounds_delta_.clear();
119 nodes_affected_by_outer_viewport_bounds_delta_.clear();
122 bool TransformTree::ComputeTransform(int source_id,
123 int dest_id,
124 gfx::Transform* transform) const {
125 transform->MakeIdentity();
127 if (source_id == dest_id)
128 return true;
130 if (source_id > dest_id) {
131 return CombineTransformsBetween(source_id, dest_id, transform);
134 return CombineInversesBetween(source_id, dest_id, transform);
137 bool TransformTree::ComputeTransformWithDestinationSublayerScale(
138 int source_id,
139 int dest_id,
140 gfx::Transform* transform) const {
141 bool success = ComputeTransform(source_id, dest_id, transform);
143 const TransformNode* dest_node = Node(dest_id);
144 if (!dest_node->data.needs_sublayer_scale)
145 return success;
147 transform->matrix().postScale(dest_node->data.sublayer_scale.x(),
148 dest_node->data.sublayer_scale.y(), 1.f);
149 return success;
152 bool TransformTree::ComputeTransformWithSourceSublayerScale(
153 int source_id,
154 int dest_id,
155 gfx::Transform* transform) const {
156 bool success = ComputeTransform(source_id, dest_id, transform);
158 const TransformNode* source_node = Node(source_id);
159 if (!source_node->data.needs_sublayer_scale)
160 return success;
162 if (source_node->data.sublayer_scale.x() == 0 ||
163 source_node->data.sublayer_scale.y() == 0)
164 return false;
166 transform->Scale(1.f / source_node->data.sublayer_scale.x(),
167 1.f / source_node->data.sublayer_scale.y());
168 return success;
171 bool TransformTree::Are2DAxisAligned(int source_id, int dest_id) const {
172 gfx::Transform transform;
173 return ComputeTransform(source_id, dest_id, &transform) &&
174 transform.Preserves2dAxisAlignment();
177 bool TransformTree::NeedsSourceToParentUpdate(TransformNode* node) {
178 return (source_to_parent_updates_allowed() &&
179 node->parent_id != node->data.source_node_id);
182 void TransformTree::UpdateTransforms(int id) {
183 TransformNode* node = Node(id);
184 TransformNode* parent_node = parent(node);
185 TransformNode* target_node = Node(node->data.target_id);
186 if (node->data.needs_local_transform_update ||
187 NeedsSourceToParentUpdate(node))
188 UpdateLocalTransform(node);
189 else
190 UndoSnapping(node);
191 UpdateScreenSpaceTransform(node, parent_node, target_node);
192 UpdateSublayerScale(node);
193 UpdateTargetSpaceTransform(node, target_node);
194 UpdateAnimationProperties(node, parent_node);
195 UpdateSnapping(node);
196 UpdateNodeAndAncestorsHaveIntegerTranslations(node, parent_node);
199 bool TransformTree::IsDescendant(int desc_id, int source_id) const {
200 while (desc_id != source_id) {
201 if (desc_id < 0)
202 return false;
203 desc_id = Node(desc_id)->parent_id;
205 return true;
208 bool TransformTree::CombineTransformsBetween(int source_id,
209 int dest_id,
210 gfx::Transform* transform) const {
211 DCHECK(source_id > dest_id);
212 const TransformNode* current = Node(source_id);
213 const TransformNode* dest = Node(dest_id);
214 // Combine transforms to and from the screen when possible. Since flattening
215 // is a non-linear operation, we cannot use this approach when there is
216 // non-trivial flattening between the source and destination nodes. For
217 // example, consider the tree R->A->B->C, where B flattens its inherited
218 // transform, and A has a non-flat transform. Suppose C is the source and A is
219 // the destination. The expected result is C * B. But C's to_screen
220 // transform is C * B * flattened(A * R), and A's from_screen transform is
221 // R^{-1} * A^{-1}. If at least one of A and R isn't flat, the inverse of
222 // flattened(A * R) won't be R^{-1} * A{-1}, so multiplying C's to_screen and
223 // A's from_screen will not produce the correct result.
224 if (!dest || (dest->data.ancestors_are_invertible &&
225 dest->data.node_and_ancestors_are_flat)) {
226 transform->ConcatTransform(current->data.to_screen);
227 if (dest)
228 transform->ConcatTransform(dest->data.from_screen);
229 return true;
232 // Flattening is defined in a way that requires it to be applied while
233 // traversing downward in the tree. We first identify nodes that are on the
234 // path from the source to the destination (this is traversing upward), and
235 // then we visit these nodes in reverse order, flattening as needed. We
236 // early-out if we get to a node whose target node is the destination, since
237 // we can then re-use the target space transform stored at that node. However,
238 // we cannot re-use a stored target space transform if the destination has a
239 // zero sublayer scale, since stored target space transforms have sublayer
240 // scale baked in, but we need to compute an unscaled transform.
241 std::vector<int> source_to_destination;
242 source_to_destination.push_back(current->id);
243 current = parent(current);
244 bool destination_has_non_zero_sublayer_scale =
245 dest->data.sublayer_scale.x() != 0.f &&
246 dest->data.sublayer_scale.y() != 0.f;
247 DCHECK(destination_has_non_zero_sublayer_scale ||
248 !dest->data.ancestors_are_invertible);
249 for (; current && current->id > dest_id; current = parent(current)) {
250 if (destination_has_non_zero_sublayer_scale &&
251 current->data.target_id == dest_id &&
252 current->data.content_target_id == dest_id)
253 break;
254 source_to_destination.push_back(current->id);
257 gfx::Transform combined_transform;
258 if (current->id > dest_id) {
259 combined_transform = current->data.to_target;
260 // The stored target space transform has sublayer scale baked in, but we
261 // need the unscaled transform.
262 combined_transform.Scale(1.0f / dest->data.sublayer_scale.x(),
263 1.0f / dest->data.sublayer_scale.y());
264 } else if (current->id < dest_id) {
265 // We have reached the lowest common ancestor of the source and destination
266 // nodes. This case can occur when we are transforming between a node
267 // corresponding to a fixed-position layer (or its descendant) and the node
268 // corresponding to the layer's render target. For example, consider the
269 // layer tree R->T->S->F where F is fixed-position, S owns a render surface,
270 // and T has a significant transform. This will yield the following
271 // transform tree:
272 // R
273 // |
274 // T
275 // /|
276 // S F
277 // In this example, T will have id 2, S will have id 3, and F will have id
278 // 4. When walking up the ancestor chain from F, the first node with a
279 // smaller id than S will be T, the lowest common ancestor of these nodes.
280 // We compute the transform from T to S here, and then from F to T in the
281 // loop below.
282 DCHECK(IsDescendant(dest_id, current->id));
283 CombineInversesBetween(current->id, dest_id, &combined_transform);
284 DCHECK(combined_transform.IsApproximatelyIdentityOrTranslation(
285 SkDoubleToMScalar(1e-4)));
288 size_t source_to_destination_size = source_to_destination.size();
289 for (size_t i = 0; i < source_to_destination_size; ++i) {
290 size_t index = source_to_destination_size - 1 - i;
291 const TransformNode* node = Node(source_to_destination[index]);
292 if (node->data.flattens_inherited_transform)
293 combined_transform.FlattenTo2d();
294 combined_transform.PreconcatTransform(node->data.to_parent);
297 transform->ConcatTransform(combined_transform);
298 return true;
301 bool TransformTree::CombineInversesBetween(int source_id,
302 int dest_id,
303 gfx::Transform* transform) const {
304 DCHECK(source_id < dest_id);
305 const TransformNode* current = Node(dest_id);
306 const TransformNode* dest = Node(source_id);
307 // Just as in CombineTransformsBetween, we can use screen space transforms in
308 // this computation only when there isn't any non-trivial flattening
309 // involved.
310 if (current->data.ancestors_are_invertible &&
311 current->data.node_and_ancestors_are_flat) {
312 transform->PreconcatTransform(current->data.from_screen);
313 if (dest)
314 transform->PreconcatTransform(dest->data.to_screen);
315 return true;
318 // Inverting a flattening is not equivalent to flattening an inverse. This
319 // means we cannot, for example, use the inverse of each node's to_parent
320 // transform, flattening where needed. Instead, we must compute the transform
321 // from the destination to the source, with flattening, and then invert the
322 // result.
323 gfx::Transform dest_to_source;
324 CombineTransformsBetween(dest_id, source_id, &dest_to_source);
325 gfx::Transform source_to_dest;
326 bool all_are_invertible = dest_to_source.GetInverse(&source_to_dest);
327 transform->PreconcatTransform(source_to_dest);
328 return all_are_invertible;
331 void TransformTree::UpdateLocalTransform(TransformNode* node) {
332 gfx::Transform transform = node->data.post_local;
333 if (NeedsSourceToParentUpdate(node)) {
334 gfx::Transform to_parent;
335 ComputeTransform(node->data.source_node_id, node->parent_id, &to_parent);
336 node->data.source_to_parent = to_parent.To2dTranslation();
339 gfx::Vector2dF fixed_position_adjustment;
340 if (node->data.affected_by_inner_viewport_bounds_delta_x)
341 fixed_position_adjustment.set_x(inner_viewport_bounds_delta_.x());
342 else if (node->data.affected_by_outer_viewport_bounds_delta_x)
343 fixed_position_adjustment.set_x(outer_viewport_bounds_delta_.x());
345 if (node->data.affected_by_inner_viewport_bounds_delta_y)
346 fixed_position_adjustment.set_y(inner_viewport_bounds_delta_.y());
347 else if (node->data.affected_by_outer_viewport_bounds_delta_y)
348 fixed_position_adjustment.set_y(outer_viewport_bounds_delta_.y());
350 transform.Translate(
351 node->data.source_to_parent.x() - node->data.scroll_offset.x() +
352 fixed_position_adjustment.x(),
353 node->data.source_to_parent.y() - node->data.scroll_offset.y() +
354 fixed_position_adjustment.y());
355 transform.PreconcatTransform(node->data.local);
356 transform.PreconcatTransform(node->data.pre_local);
357 node->data.set_to_parent(transform);
358 node->data.needs_local_transform_update = false;
361 void TransformTree::UpdateScreenSpaceTransform(TransformNode* node,
362 TransformNode* parent_node,
363 TransformNode* target_node) {
364 if (!parent_node) {
365 node->data.to_screen = node->data.to_parent;
366 node->data.ancestors_are_invertible = true;
367 node->data.to_screen_is_animated = false;
368 node->data.node_and_ancestors_are_flat = node->data.to_parent.IsFlat();
369 } else {
370 node->data.to_screen = parent_node->data.to_screen;
371 if (node->data.flattens_inherited_transform)
372 node->data.to_screen.FlattenTo2d();
373 node->data.to_screen.PreconcatTransform(node->data.to_parent);
374 node->data.ancestors_are_invertible =
375 parent_node->data.ancestors_are_invertible;
376 node->data.node_and_ancestors_are_flat =
377 parent_node->data.node_and_ancestors_are_flat &&
378 node->data.to_parent.IsFlat();
381 if (!node->data.to_screen.GetInverse(&node->data.from_screen))
382 node->data.ancestors_are_invertible = false;
385 void TransformTree::UpdateSublayerScale(TransformNode* node) {
386 // The sublayer scale depends on the screen space transform, so update it too.
387 node->data.sublayer_scale =
388 node->data.needs_sublayer_scale
389 ? MathUtil::ComputeTransform2dScaleComponents(
390 node->data.to_screen, node->data.layer_scale_factor)
391 : gfx::Vector2dF(1.0f, 1.0f);
394 void TransformTree::UpdateTargetSpaceTransform(TransformNode* node,
395 TransformNode* target_node) {
396 if (node->data.needs_sublayer_scale) {
397 node->data.to_target.MakeIdentity();
398 node->data.to_target.Scale(node->data.sublayer_scale.x(),
399 node->data.sublayer_scale.y());
400 } else {
401 // In order to include the root transform for the root surface, we walk up
402 // to the root of the transform tree in ComputeTransform.
403 int target_id = target_node->id;
404 ComputeTransformWithDestinationSublayerScale(node->id, target_id,
405 &node->data.to_target);
408 if (!node->data.to_target.GetInverse(&node->data.from_target))
409 node->data.ancestors_are_invertible = false;
412 void TransformTree::UpdateAnimationProperties(TransformNode* node,
413 TransformNode* parent_node) {
414 bool ancestor_is_animating = false;
415 bool ancestor_is_animating_scale = false;
416 float ancestor_maximum_target_scale = 0.f;
417 float ancestor_starting_animation_scale = 0.f;
418 if (parent_node) {
419 ancestor_is_animating = parent_node->data.to_screen_is_animated;
420 ancestor_is_animating_scale =
421 parent_node->data.to_screen_has_scale_animation;
422 ancestor_maximum_target_scale =
423 parent_node->data.combined_maximum_animation_target_scale;
424 ancestor_starting_animation_scale =
425 parent_node->data.combined_starting_animation_scale;
427 node->data.to_screen_is_animated =
428 node->data.is_animated || ancestor_is_animating;
429 node->data.to_screen_has_scale_animation =
430 !node->data.has_only_translation_animations ||
431 ancestor_is_animating_scale;
433 // Once we've failed to compute a maximum animated scale at an ancestor, we
434 // continue to fail.
435 bool failed_at_ancestor =
436 ancestor_is_animating_scale && ancestor_maximum_target_scale == 0.f;
438 // Computing maximum animated scale in the presence of non-scale/translation
439 // transforms isn't supported.
440 bool failed_for_non_scale_or_translation =
441 !node->data.to_target.IsScaleOrTranslation();
443 // We don't attempt to accumulate animation scale from multiple nodes with
444 // scale animations, because of the risk of significant overestimation. For
445 // example, one node might be increasing scale from 1 to 10 at the same time
446 // as another node is decreasing scale from 10 to 1. Naively combining these
447 // scales would produce a scale of 100.
448 bool failed_for_multiple_scale_animations =
449 ancestor_is_animating_scale &&
450 !node->data.has_only_translation_animations;
452 if (failed_at_ancestor || failed_for_non_scale_or_translation ||
453 failed_for_multiple_scale_animations) {
454 node->data.combined_maximum_animation_target_scale = 0.f;
455 node->data.combined_starting_animation_scale = 0.f;
457 // This ensures that descendants know we've failed to compute a maximum
458 // animated scale.
459 node->data.to_screen_has_scale_animation = true;
460 return;
463 if (!node->data.to_screen_has_scale_animation) {
464 node->data.combined_maximum_animation_target_scale = 0.f;
465 node->data.combined_starting_animation_scale = 0.f;
466 return;
469 // At this point, we know exactly one of this node or an ancestor is animating
470 // scale.
471 if (node->data.has_only_translation_animations) {
472 // An ancestor is animating scale.
473 gfx::Vector2dF local_scales =
474 MathUtil::ComputeTransform2dScaleComponents(node->data.local, 0.f);
475 float max_local_scale = std::max(local_scales.x(), local_scales.y());
476 node->data.combined_maximum_animation_target_scale =
477 max_local_scale * ancestor_maximum_target_scale;
478 node->data.combined_starting_animation_scale =
479 max_local_scale * ancestor_starting_animation_scale;
480 return;
483 if (node->data.local_starting_animation_scale == 0.f ||
484 node->data.local_maximum_animation_target_scale == 0.f) {
485 node->data.combined_maximum_animation_target_scale = 0.f;
486 node->data.combined_starting_animation_scale = 0.f;
487 return;
490 gfx::Vector2dF ancestor_scales =
491 parent_node ? MathUtil::ComputeTransform2dScaleComponents(
492 parent_node->data.to_target, 0.f)
493 : gfx::Vector2dF(1.f, 1.f);
494 float max_ancestor_scale = std::max(ancestor_scales.x(), ancestor_scales.y());
495 node->data.combined_maximum_animation_target_scale =
496 max_ancestor_scale * node->data.local_maximum_animation_target_scale;
497 node->data.combined_starting_animation_scale =
498 max_ancestor_scale * node->data.local_starting_animation_scale;
501 void TransformTree::UndoSnapping(TransformNode* node) {
502 // to_parent transform has the scroll snap from previous frame baked in.
503 // We need to undo it and use the un-snapped transform to compute current
504 // target and screen space transforms.
505 node->data.to_parent.Translate(-node->data.scroll_snap.x(),
506 -node->data.scroll_snap.y());
509 void TransformTree::UpdateSnapping(TransformNode* node) {
510 if (!node->data.scrolls || node->data.to_screen_is_animated ||
511 !node->data.to_target.IsScaleOrTranslation()) {
512 return;
515 // Scroll snapping must be done in target space (the pixels we care about).
516 // This means we effectively snap the target space transform. If TT is the
517 // target space transform and TT' is TT with its translation components
518 // rounded, then what we're after is the scroll delta X, where TT * X = TT'.
519 // I.e., we want a transform that will realize our scroll snap. It follows
520 // that X = TT^-1 * TT'. We cache TT and TT^-1 to make this more efficient.
521 gfx::Transform rounded = node->data.to_target;
522 rounded.RoundTranslationComponents();
523 gfx::Transform delta = node->data.from_target;
524 delta *= rounded;
526 DCHECK(delta.IsApproximatelyIdentityOrTranslation(SkDoubleToMScalar(1e-4)))
527 << delta.ToString();
529 gfx::Vector2dF translation = delta.To2dTranslation();
531 // Now that we have our scroll delta, we must apply it to each of our
532 // combined, to/from matrices.
533 node->data.to_target = rounded;
534 node->data.to_parent.Translate(translation.x(), translation.y());
535 node->data.from_target.matrix().postTranslate(-translation.x(),
536 -translation.y(), 0);
537 node->data.to_screen.Translate(translation.x(), translation.y());
538 node->data.from_screen.matrix().postTranslate(-translation.x(),
539 -translation.y(), 0);
541 node->data.scroll_snap = translation;
544 void TransformTree::SetInnerViewportBoundsDelta(gfx::Vector2dF bounds_delta) {
545 if (inner_viewport_bounds_delta_ == bounds_delta)
546 return;
548 inner_viewport_bounds_delta_ = bounds_delta;
550 if (nodes_affected_by_inner_viewport_bounds_delta_.empty())
551 return;
553 set_needs_update(true);
554 for (int i : nodes_affected_by_inner_viewport_bounds_delta_)
555 Node(i)->data.needs_local_transform_update = true;
558 void TransformTree::SetOuterViewportBoundsDelta(gfx::Vector2dF bounds_delta) {
559 if (outer_viewport_bounds_delta_ == bounds_delta)
560 return;
562 outer_viewport_bounds_delta_ = bounds_delta;
564 if (nodes_affected_by_outer_viewport_bounds_delta_.empty())
565 return;
567 set_needs_update(true);
568 for (int i : nodes_affected_by_outer_viewport_bounds_delta_)
569 Node(i)->data.needs_local_transform_update = true;
572 void TransformTree::AddNodeAffectedByInnerViewportBoundsDelta(int node_id) {
573 nodes_affected_by_inner_viewport_bounds_delta_.push_back(node_id);
576 void TransformTree::AddNodeAffectedByOuterViewportBoundsDelta(int node_id) {
577 nodes_affected_by_outer_viewport_bounds_delta_.push_back(node_id);
580 bool TransformTree::HasNodesAffectedByInnerViewportBoundsDelta() const {
581 return !nodes_affected_by_inner_viewport_bounds_delta_.empty();
584 bool TransformTree::HasNodesAffectedByOuterViewportBoundsDelta() const {
585 return !nodes_affected_by_outer_viewport_bounds_delta_.empty();
588 void EffectTree::UpdateOpacities(int id) {
589 EffectNode* node = Node(id);
590 node->data.screen_space_opacity = node->data.opacity;
592 EffectNode* parent_node = parent(node);
593 if (parent_node)
594 node->data.screen_space_opacity *= parent_node->data.screen_space_opacity;
597 void TransformTree::UpdateNodeAndAncestorsHaveIntegerTranslations(
598 TransformNode* node,
599 TransformNode* parent_node) {
600 node->data.node_and_ancestors_have_only_integer_translation =
601 node->data.to_parent.IsIdentityOrIntegerTranslation();
602 if (parent_node)
603 node->data.node_and_ancestors_have_only_integer_translation =
604 node->data.node_and_ancestors_have_only_integer_translation &&
605 parent_node->data.node_and_ancestors_have_only_integer_translation;
608 void ClipTree::SetViewportClip(gfx::RectF viewport_rect) {
609 if (size() < 2)
610 return;
611 ClipNode* node = Node(1);
612 if (viewport_rect == node->data.clip)
613 return;
614 node->data.clip = viewport_rect;
615 set_needs_update(true);
618 gfx::RectF ClipTree::ViewportClip() {
619 const unsigned long min_size = 1;
620 DCHECK_GT(size(), min_size);
621 return Node(1)->data.clip;
624 PropertyTrees::PropertyTrees() : needs_rebuild(true), sequence_number(0) {
627 } // namespace cc