From a92fdc171986e8582bd945051c0acf60aa7969dc Mon Sep 17 00:00:00 2001 From: ajuma Date: Tue, 31 Mar 2015 15:47:41 -0700 Subject: [PATCH] cc: Fix visible rects of animated layers with singular transforms Layers with singular transforms are usually skipped when computing visible content rects. However, animated layers with singular transforms cannot be skipped, since their transforms may become non-singular during the animation (so these layers still need to be recorded). Currently, CalcDrawProps treats such layers as fully visible if they have a non-empty visible rect in clipped target space. Layers with an empty visible rect in clipped target space are treated as having an empty visible content rect. This CL makes property trees match CDP's behavior. BUG=470244 Review URL: https://codereview.chromium.org/1041663003 Cr-Commit-Position: refs/heads/master@{#323132} --- cc/trees/draw_property_utils.cc | 79 ++++++++++++++++++++--------- cc/trees/layer_tree_host_common_unittest.cc | 77 ++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 23 deletions(-) diff --git a/cc/trees/draw_property_utils.cc b/cc/trees/draw_property_utils.cc index 26453a3124ab..503270ae91a2 100644 --- a/cc/trees/draw_property_utils.cc +++ b/cc/trees/draw_property_utils.cc @@ -45,49 +45,74 @@ void CalculateVisibleRects( target_is_root_surface ? 0 : transform_node->data.content_target_id; const TransformNode* target_node = transform_tree.Node(target_id); - gfx::Transform clip_to_target; - gfx::Transform content_to_target; - gfx::Transform target_to_content; - gfx::Transform target_to_layer; - // TODO(ajuma): Try to re-use transforms already stored in the transform // tree instead of computing transforms below. - bool success = - transform_tree.ComputeTransform(clip_transform_node->id, - target_node->id, &clip_to_target) && - transform_tree.ComputeTransform(transform_node->id, target_node->id, - &content_to_target) && - transform_tree.ComputeTransform(target_node->id, transform_node->id, - &target_to_layer); - - // This should only fail if we somehow got here with a singular ancestor. + gfx::Transform content_to_target; + bool success = transform_tree.ComputeTransform( + transform_node->id, target_node->id, &content_to_target); DCHECK(success); - target_to_content.Scale(contents_scale_x, contents_scale_y); - target_to_content.Translate(-layer->offset_to_transform_parent().x(), - -layer->offset_to_transform_parent().y()); - target_to_content.PreconcatTransform(target_to_layer); - content_to_target.Translate(layer->offset_to_transform_parent().x(), layer->offset_to_transform_parent().y()); content_to_target.Scale(1.0 / contents_scale_x, 1.0 / contents_scale_y); - gfx::Rect layer_content_rect = gfx::Rect(layer_content_bounds); - gfx::Rect layer_content_bounds_in_target_space = - MathUtil::MapEnclosingClippedRect(content_to_target, - layer_content_rect); gfx::Rect clip_rect_in_target_space; + gfx::Transform clip_to_target; + success = transform_tree.ComputeTransform( + clip_transform_node->id, target_node->id, &clip_to_target); if (target_node->id > clip_node->data.transform_id) { + if (!success) { + DCHECK(target_node->data.to_screen_is_animated); + + // An animated singular transform may become non-singular during the + // animation, so we still need to compute a visible rect. In this + // situation, we treat the entire layer as visible. + layer->set_visible_rect_from_property_trees( + gfx::Rect(layer_content_bounds)); + continue; + } + clip_rect_in_target_space = gfx::ToEnclosingRect(MathUtil::ProjectClippedRect( clip_to_target, clip_node->data.combined_clip)); } else { + // Computing a transform to an ancestor should always succeed. + DCHECK(success); clip_rect_in_target_space = gfx::ToEnclosingRect(MathUtil::MapClippedRect( clip_to_target, clip_node->data.combined_clip)); } + gfx::Rect layer_content_rect = gfx::Rect(layer_content_bounds); + gfx::Rect layer_content_bounds_in_target_space = + MathUtil::MapEnclosingClippedRect(content_to_target, + layer_content_rect); clip_rect_in_target_space.Intersect(layer_content_bounds_in_target_space); + if (clip_rect_in_target_space.IsEmpty()) { + layer->set_visible_rect_from_property_trees(gfx::Rect()); + continue; + } + + gfx::Transform target_to_content; + gfx::Transform target_to_layer; + + success = transform_tree.ComputeTransform( + target_node->id, transform_node->id, &target_to_layer); + if (!success) { + DCHECK(transform_node->data.to_screen_is_animated); + + // An animated singular transform may become non-singular during the + // animation, so we still need to compute a visible rect. In this + // situation, we treat the entire layer as visible. + layer->set_visible_rect_from_property_trees( + gfx::Rect(layer_content_bounds)); + continue; + } + + target_to_content.Scale(contents_scale_x, contents_scale_y); + target_to_content.Translate(-layer->offset_to_transform_parent().x(), + -layer->offset_to_transform_parent().y()); + target_to_content.PreconcatTransform(target_to_layer); gfx::Rect visible_rect = MathUtil::ProjectEnclosingClippedRect( target_to_content, clip_rect_in_target_space); @@ -158,7 +183,15 @@ static bool IsBackFaceInvisible(Layer* layer, const TransformTree& tree) { IsLayerBackFaceExposed(backface_test_layer, tree); } +static bool IsAnimatingTransformToScreen(Layer* layer, + const TransformTree& tree) { + const TransformNode* node = tree.Node(layer->transform_tree_index()); + return node->data.to_screen_is_animated; +} + static bool IsInvisibleDueToTransform(Layer* layer, const TransformTree& tree) { + if (IsAnimatingTransformToScreen(layer, tree)) + return false; return HasSingularTransform(layer, tree) || IsBackFaceInvisible(layer, tree); } diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index ab4d703db555..439d9200a911 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc @@ -8809,6 +8809,83 @@ TEST_F(LayerTreeHostCommonTest, VisibleContentRectForAnimatedLayer) { EXPECT_FALSE(animated->visible_rect_from_property_trees().IsEmpty()); } +TEST_F(LayerTreeHostCommonTest, + VisibleContentRectForAnimatedLayerWithSingularTransform) { + const gfx::Transform identity_matrix; + scoped_refptr root = Layer::Create(); + scoped_refptr clip = Layer::Create(); + scoped_refptr animated = + make_scoped_refptr(new LayerWithForcedDrawsContent()); + scoped_refptr surface = + make_scoped_refptr(new LayerWithForcedDrawsContent()); + scoped_refptr descendant_of_animation = + make_scoped_refptr(new LayerWithForcedDrawsContent()); + + root->AddChild(clip); + clip->AddChild(animated); + animated->AddChild(surface); + surface->AddChild(descendant_of_animation); + + clip->SetMasksToBounds(true); + surface->SetForceRenderSurface(true); + + scoped_ptr host(CreateFakeLayerTreeHost()); + host->SetRootLayer(root); + + gfx::Transform uninvertible_matrix; + uninvertible_matrix.Scale3d(6.f, 6.f, 0.f); + + SetLayerPropertiesForTesting(root.get(), identity_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(100, 100), true, false); + SetLayerPropertiesForTesting(clip.get(), identity_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(10, 10), true, false); + SetLayerPropertiesForTesting(animated.get(), uninvertible_matrix, + gfx::Point3F(), gfx::PointF(), + gfx::Size(120, 120), true, false); + SetLayerPropertiesForTesting(surface.get(), identity_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(100, 100), true, false); + SetLayerPropertiesForTesting(descendant_of_animation.get(), identity_matrix, + gfx::Point3F(), gfx::PointF(), + gfx::Size(200, 200), true, false); + + TransformOperations start_transform_operations; + start_transform_operations.AppendMatrix(uninvertible_matrix); + TransformOperations end_transform_operations; + + AddAnimatedTransformToLayer(animated.get(), 10.0, start_transform_operations, + end_transform_operations); + + ExecuteCalculateDrawProperties(root.get()); + + // The animated layer has a singular transform and maps to a non-empty rect in + // clipped target space, so is treated as fully visible. + EXPECT_EQ(gfx::Rect(120, 120), animated->visible_rect_from_property_trees()); + + // The singular transform on |animated| is flattened when inherited by + // |surface|, and this happens to make it invertible. + EXPECT_EQ(gfx::Rect(2, 2), surface->visible_rect_from_property_trees()); + EXPECT_EQ(gfx::Rect(2, 2), + descendant_of_animation->visible_rect_from_property_trees()); + + gfx::Transform zero_matrix; + zero_matrix.Scale3d(0.f, 0.f, 0.f); + SetLayerPropertiesForTesting(animated.get(), zero_matrix, gfx::Point3F(), + gfx::PointF(), gfx::Size(120, 120), true, false); + + ExecuteCalculateDrawProperties(root.get()); + + // The animated layer maps to the empty rect in clipped target space, so is + // treated as having an empty visible rect. + EXPECT_EQ(gfx::Rect(), animated->visible_rect_from_property_trees()); + + // This time, flattening does not make |animated|'s transform invertible. This + // means the clip cannot be projected into |surface|'s space, so we treat + // |surface| and layers that draw into it as fully visible. + EXPECT_EQ(gfx::Rect(100, 100), surface->visible_rect_from_property_trees()); + EXPECT_EQ(gfx::Rect(200, 200), + descendant_of_animation->visible_rect_from_property_trees()); +} + // Verify that having an animated filter (but no current filter, as these // are mutually exclusive) correctly creates a render surface. TEST_F(LayerTreeHostCommonTest, AnimatedFilterCreatesRenderSurface) { -- 2.11.4.GIT