diff options
Diffstat (limited to 'chromium/cc/trees/property_tree_builder_unittest.cc')
-rw-r--r-- | chromium/cc/trees/property_tree_builder_unittest.cc | 1954 |
1 files changed, 1954 insertions, 0 deletions
diff --git a/chromium/cc/trees/property_tree_builder_unittest.cc b/chromium/cc/trees/property_tree_builder_unittest.cc new file mode 100644 index 00000000000..9b4a5f348b0 --- /dev/null +++ b/chromium/cc/trees/property_tree_builder_unittest.cc @@ -0,0 +1,1954 @@ +// Copyright 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/trees/property_tree_builder.h" + +#include "cc/animation/keyframed_animation_curve.h" +#include "cc/layers/layer.h" +#include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" +#include "cc/layers/render_surface_impl.h" +#include "cc/layers/texture_layer.h" +#include "cc/test/animation_test_common.h" +#include "cc/test/fake_content_layer_client.h" +#include "cc/test/layer_tree_impl_test_base.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/geometry/vector2d_conversions.h" +#include "ui/gfx/transform.h" + +namespace cc { +namespace { + +class PropertyTreeBuilderTest : public LayerTreeImplTestBase, + public testing::Test { + public: + PropertyTreeBuilderTest() : LayerTreeImplTestBase(LayerTreeSettings()) {} + + void UpdateMainDrawProperties(float device_scale_factor = 1.0f) { + SetDeviceScaleAndUpdateViewportRect(host(), device_scale_factor); + UpdateDrawProperties(host()); + } + + LayerImpl* ImplOf(const scoped_refptr<Layer>& layer) { + return layer ? host_impl()->active_tree()->LayerById(layer->id()) : nullptr; + } + RenderSurfaceImpl* GetRenderSurfaceImpl(const scoped_refptr<Layer>& layer) { + return GetRenderSurface(ImplOf(layer)); + } + + // Updates main thread draw properties, commits main thread tree to + // impl-side pending tree, and updates pending tree draw properties. + void Commit(float device_scale_factor = 1.0f) { + UpdateMainDrawProperties(device_scale_factor); + if (!host_impl()->pending_tree()) + host_impl()->CreatePendingTree(); + host()->CommitAndCreatePendingTree(); + // TODO(https://crbug.com/939968) This call should be handled by + // FakeLayerTreeHost instead of manually pushing the properties from the + // layer tree host to the pending tree. + host()->PushLayerTreePropertiesTo(host_impl()->pending_tree()); + + UpdateDrawProperties(host_impl()->pending_tree()); + } + + // Calls Commit(), then activates the pending tree, and updates active tree + // draw properties. + void CommitAndActivate(float device_scale_factor = 1.0f) { + Commit(device_scale_factor); + host_impl()->ActivateSyncTree(); + DCHECK_EQ(device_scale_factor, + host_impl()->active_tree()->device_scale_factor()); + UpdateActiveTreeDrawProperties(device_scale_factor); + } + + const RenderSurfaceList& GetRenderSurfaceList() { + return host_impl()->active_tree()->GetRenderSurfaceList(); + } +}; + +TEST_F(PropertyTreeBuilderTest, EffectTreeTransformIdTest) { + // Tests that effect tree node gets a valid transform id when a layer + // has opacity but doesn't create a render surface. + auto parent = Layer::Create(); + host()->SetRootLayer(parent); + auto child = Layer::Create(); + parent->AddChild(child); + child->SetIsDrawable(true); + + parent->SetBounds(gfx::Size(100, 100)); + child->SetPosition(gfx::PointF(10, 10)); + child->SetBounds(gfx::Size(100, 100)); + child->SetOpacity(0.f); + UpdateMainDrawProperties(); + EffectNode* node = GetEffectNode(child.get()); + const int transform_tree_size = + GetPropertyTrees(parent.get())->transform_tree.next_available_id(); + EXPECT_LT(node->transform_id, transform_tree_size); +} + +TEST_F(PropertyTreeBuilderTest, RenderSurfaceForNonAxisAlignedClipping) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto rotated_and_transparent = Layer::Create(); + root->AddChild(rotated_and_transparent); + auto clips_subtree = Layer::Create(); + rotated_and_transparent->AddChild(clips_subtree); + auto draws_content = Layer::Create(); + clips_subtree->AddChild(draws_content); + + root->SetBounds(gfx::Size(10, 10)); + rotated_and_transparent->SetBounds(gfx::Size(10, 10)); + rotated_and_transparent->SetOpacity(0.5f); + gfx::Transform rotate; + rotate.Rotate(2); + rotated_and_transparent->SetTransform(rotate); + clips_subtree->SetBounds(gfx::Size(10, 10)); + clips_subtree->SetMasksToBounds(true); + draws_content->SetBounds(gfx::Size(10, 10)); + draws_content->SetIsDrawable(true); + + UpdateMainDrawProperties(); + EXPECT_TRUE(GetEffectNode(clips_subtree.get())->HasRenderSurface()); +} + +TEST_F(PropertyTreeBuilderTest, EffectNodesForNonAxisAlignedClips) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto rotate_and_clip = Layer::Create(); + root->AddChild(rotate_and_clip); + auto only_clip = Layer::Create(); + rotate_and_clip->AddChild(only_clip); + auto rotate_and_clip2 = Layer::Create(); + only_clip->AddChild(rotate_and_clip2); + + gfx::Transform rotate; + rotate.Rotate(2); + root->SetBounds(gfx::Size(10, 10)); + rotate_and_clip->SetBounds(gfx::Size(10, 10)); + rotate_and_clip->SetTransform(rotate); + rotate_and_clip->SetMasksToBounds(true); + only_clip->SetBounds(gfx::Size(10, 10)); + only_clip->SetMasksToBounds(true); + rotate_and_clip2->SetBounds(gfx::Size(10, 10)); + rotate_and_clip2->SetTransform(rotate); + rotate_and_clip2->SetMasksToBounds(true); + + UpdateMainDrawProperties(); + // non-axis aligned clip should create an effect node + EXPECT_NE(root->effect_tree_index(), rotate_and_clip->effect_tree_index()); + // Since only_clip's clip is in the same non-axis aligned space as + // rotate_and_clip's clip, no new effect node should be created. + EXPECT_EQ(rotate_and_clip->effect_tree_index(), + only_clip->effect_tree_index()); + // rotate_and_clip2's clip and only_clip's clip are in different non-axis + // aligned spaces. So, new effect node should be created. + EXPECT_NE(rotate_and_clip2->effect_tree_index(), + only_clip->effect_tree_index()); +} + +TEST_F(PropertyTreeBuilderTest, + RenderSurfaceListForRenderSurfaceWithClippedLayer) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto render_surface1 = Layer::Create(); + root->AddChild(render_surface1); + auto child = Layer::Create(); + render_surface1->AddChild(child); + + root->SetBounds(gfx::Size(10, 10)); + root->SetMasksToBounds(true); + render_surface1->SetBounds(gfx::Size(10, 10)); + render_surface1->SetForceRenderSurfaceForTesting(true); + child->SetIsDrawable(true); + child->SetPosition(gfx::PointF(30.f, 30.f)); + child->SetBounds(gfx::Size(10, 10)); + + CommitAndActivate(); + + // The child layer's content is entirely outside the root's clip rect, so + // the intermediate render surface should not be listed here, even if it was + // forced to be created. Render surfaces without children or visible content + // are unexpected at draw time (e.g. we might try to create a content texture + // of size 0). + ASSERT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_EQ(1U, GetRenderSurfaceList().size()); +} + +TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTransparentChild) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto render_surface1 = Layer::Create(); + root->AddChild(render_surface1); + auto child = Layer::Create(); + render_surface1->AddChild(child); + + root->SetBounds(gfx::Size(10, 10)); + render_surface1->SetBounds(gfx::Size(10, 10)); + render_surface1->SetForceRenderSurfaceForTesting(true); + render_surface1->SetOpacity(0.f); + child->SetBounds(gfx::Size(10, 10)); + child->SetIsDrawable(true); + + CommitAndActivate(); + + // Since the layer is transparent, render_surface1_impl->GetRenderSurface() + // should not have gotten added anywhere. Also, the drawable content rect + // should not have been extended by the children. + ASSERT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_EQ(0, GetRenderSurfaceImpl(root)->num_contributors()); + EXPECT_EQ(1U, GetRenderSurfaceList().size()); + EXPECT_EQ(static_cast<viz::RenderPassId>(root->id()), + GetRenderSurfaceList().at(0)->id()); + EXPECT_EQ(gfx::Rect(), ImplOf(root)->drawable_content_rect()); +} + +TEST_F(PropertyTreeBuilderTest, + RenderSurfaceListForTransparentChildWithBackdropFilter) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto render_surface1 = Layer::Create(); + root->AddChild(render_surface1); + auto child = Layer::Create(); + render_surface1->AddChild(child); + + root->SetBounds(gfx::Size(10, 10)); + render_surface1->SetBounds(gfx::Size(10, 10)); + render_surface1->SetForceRenderSurfaceForTesting(true); + render_surface1->SetOpacity(0.f); + render_surface1->SetIsDrawable(true); + FilterOperations filters; + filters.Append(FilterOperation::CreateBlurFilter(1.5f)); + render_surface1->SetBackdropFilters(filters); + child->SetBounds(gfx::Size(10, 10)); + child->SetIsDrawable(true); + host()->SetElementIdsForTesting(); + + CommitAndActivate(); + EXPECT_EQ(2U, GetRenderSurfaceList().size()); + + // The layer is fully transparent, but has a backdrop filter, so it + // shouldn't be skipped and should be drawn. + ASSERT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_EQ(1, GetRenderSurfaceImpl(root)->num_contributors()); + EXPECT_EQ(gfx::RectF(0, 0, 10, 10), + GetRenderSurfaceImpl(root)->DrawableContentRect()); + EXPECT_TRUE(GetEffectNode(ImplOf(render_surface1))->is_drawn); + + // When root is transparent, the layer should not be drawn. + host_impl()->active_tree()->SetOpacityMutated(root->element_id(), 0.f); + host_impl()->active_tree()->SetOpacityMutated(render_surface1->element_id(), + 1.f); + ImplOf(render_surface1)->set_visible_layer_rect(gfx::Rect()); + UpdateActiveTreeDrawProperties(); + + EXPECT_FALSE(GetEffectNode(ImplOf(render_surface1))->is_drawn); + EXPECT_EQ(gfx::Rect(), ImplOf(render_surface1)->visible_layer_rect()); +} + +TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForFilter) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto parent = Layer::Create(); + root->AddChild(parent); + auto child1 = Layer::Create(); + parent->AddChild(child1); + auto child2 = Layer::Create(); + parent->AddChild(child2); + + gfx::Transform scale_matrix; + scale_matrix.Scale(2.0f, 2.0f); + + root->SetBounds(gfx::Size(100, 100)); + parent->SetTransform(scale_matrix); + FilterOperations filters; + filters.Append(FilterOperation::CreateBlurFilter(10.0f)); + parent->SetFilters(filters); + parent->SetForceRenderSurfaceForTesting(true); + child1->SetBounds(gfx::Size(25, 25)); + child1->SetIsDrawable(true); + child1->SetForceRenderSurfaceForTesting(true); + child2->SetPosition(gfx::PointF(25, 25)); + child2->SetBounds(gfx::Size(25, 25)); + child2->SetIsDrawable(true); + child2->SetForceRenderSurfaceForTesting(true); + + CommitAndActivate(); + + ASSERT_TRUE(GetRenderSurfaceImpl(parent)); + EXPECT_EQ(2, GetRenderSurfaceImpl(parent)->num_contributors()); + EXPECT_EQ(4U, GetRenderSurfaceList().size()); + + // The rectangle enclosing child1 and child2 (0,0 50x50), expanded for the + // blur (-30,-30 110x110), and then scaled by the scale matrix + // (-60,-60 220x220). + EXPECT_EQ(gfx::RectF(-60, -60, 220, 220), + GetRenderSurfaceImpl(parent)->DrawableContentRect()); +} + +TEST_F(PropertyTreeBuilderTest, ForceRenderSurface) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto render_surface1 = Layer::Create(); + root->AddChild(render_surface1); + auto child = Layer::Create(); + render_surface1->AddChild(child); + + root->SetBounds(gfx::Size(10, 10)); + render_surface1->SetBounds(gfx::Size(10, 10)); + render_surface1->SetForceRenderSurfaceForTesting(true); + child->SetBounds(gfx::Size(10, 10)); + child->SetIsDrawable(true); + + CommitAndActivate(); + + // The root layer always creates a render surface + EXPECT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(render_surface1), GetRenderSurfaceImpl(root)); + + render_surface1->SetForceRenderSurfaceForTesting(false); + CommitAndActivate(); + + EXPECT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(render_surface1), GetRenderSurfaceImpl(root)); +} + +TEST_F(PropertyTreeBuilderTest, VisibleRectWithClippingAndFilters) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto clip = Layer::Create(); + root->AddChild(clip); + auto filter = Layer::Create(); + clip->AddChild(filter); + auto filter_child = Layer::Create(); + filter->AddChild(filter_child); + + root->SetBounds(gfx::Size(100, 100)); + clip->SetBounds(gfx::Size(10, 10)); + filter->SetForceRenderSurfaceForTesting(true); + filter_child->SetBounds(gfx::Size(2000, 2000)); + filter_child->SetPosition(gfx::PointF(-50, -50)); + filter_child->SetIsDrawable(true); + + clip->SetMasksToBounds(true); + + CommitAndActivate(); + + EXPECT_EQ(gfx::Rect(50, 50, 10, 10), + ImplOf(filter_child)->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(10, 10), GetRenderSurfaceImpl(filter)->content_rect()); + + FilterOperations blur_filter; + blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f)); + filter->SetFilters(blur_filter); + + CommitAndActivate(); + + EXPECT_EQ(gfx::Rect(38, 38, 34, 34), + ImplOf(filter_child)->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(-12, -12, 34, 34), + GetRenderSurfaceImpl(filter)->content_rect()); + + gfx::Transform vertical_flip; + vertical_flip.Scale(1, -1); + sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>( + vertical_flip.matrix(), kLow_SkFilterQuality, nullptr); + FilterOperations reflection_filter; + reflection_filter.Append( + FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( + SkBlendMode::kSrcOver, std::move(flip_filter), nullptr))); + filter->SetFilters(reflection_filter); + + CommitAndActivate(); + + EXPECT_EQ(gfx::Rect(49, 39, 12, 21), + ImplOf(filter_child)->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(-1, -11, 12, 21), + GetRenderSurfaceImpl(filter)->content_rect()); +} + +TEST_F(PropertyTreeBuilderTest, VisibleRectWithScalingClippingAndFilters) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto scale = Layer::Create(); + root->AddChild(scale); + auto clip = Layer::Create(); + scale->AddChild(clip); + auto filter = Layer::Create(); + clip->AddChild(filter); + auto filter_child = Layer::Create(); + filter->AddChild(filter_child); + + root->SetBounds(gfx::Size(100, 100)); + clip->SetBounds(gfx::Size(10, 10)); + filter->SetForceRenderSurfaceForTesting(true); + filter_child->SetBounds(gfx::Size(2000, 2000)); + filter_child->SetPosition(gfx::PointF(-50, -50)); + filter_child->SetIsDrawable(true); + + clip->SetMasksToBounds(true); + + gfx::Transform scale_transform; + scale_transform.Scale(3, 3); + scale->SetTransform(scale_transform); + + CommitAndActivate(); + + EXPECT_EQ(gfx::Rect(50, 50, 10, 10), + ImplOf(filter_child)->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(30, 30), GetRenderSurfaceImpl(filter)->content_rect()); + + FilterOperations blur_filter; + blur_filter.Append(FilterOperation::CreateBlurFilter(4.0f)); + filter->SetFilters(blur_filter); + + CommitAndActivate(); + + EXPECT_EQ(gfx::Rect(38, 38, 34, 34), + ImplOf(filter_child)->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(-36, -36, 102, 102), + GetRenderSurfaceImpl(filter)->content_rect()); + + gfx::Transform vertical_flip; + vertical_flip.Scale(1, -1); + sk_sp<PaintFilter> flip_filter = sk_make_sp<MatrixPaintFilter>( + vertical_flip.matrix(), kLow_SkFilterQuality, nullptr); + FilterOperations reflection_filter; + reflection_filter.Append( + FilterOperation::CreateReferenceFilter(sk_make_sp<XfermodePaintFilter>( + SkBlendMode::kSrcOver, std::move(flip_filter), nullptr))); + filter->SetFilters(reflection_filter); + + CommitAndActivate(); + + EXPECT_EQ(gfx::Rect(49, 39, 12, 21), + ImplOf(filter_child)->visible_layer_rect()); + EXPECT_EQ(gfx::Rect(-1, -31, 32, 61), + GetRenderSurfaceImpl(filter)->content_rect()); +} + +TEST_F(PropertyTreeBuilderTest, TextureLayerSnapping) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto child = TextureLayer::CreateForMailbox(nullptr); + root->AddChild(child); + + root->SetBounds(gfx::Size(100, 100)); + child->SetBounds(gfx::Size(100, 100)); + child->SetIsDrawable(true); + gfx::Transform fractional_translate; + fractional_translate.Translate(10.5f, 20.3f); + child->SetTransform(fractional_translate); + + CommitAndActivate(); + + auto child_screen_space_transform = ImplOf(child)->ScreenSpaceTransform(); + EXPECT_NE(child_screen_space_transform, fractional_translate); + fractional_translate.RoundTranslationComponents(); + EXPECT_TRANSFORMATION_MATRIX_EQ(child_screen_space_transform, + fractional_translate); + gfx::RectF layer_bounds_in_screen_space = MathUtil::MapClippedRect( + child_screen_space_transform, gfx::RectF(gfx::SizeF(child->bounds()))); + EXPECT_EQ(layer_bounds_in_screen_space, gfx::RectF(11.f, 20.f, 100.f, 100.f)); +} + +// Verify the behavior of back-face culling when there are no preserve-3d +// layers. Note that 3d transforms still apply in this case, but they are +// "flattened" to each parent layer according to current W3C spec. +TEST_F(PropertyTreeBuilderTest, BackFaceCullingWithoutPreserves3d) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto front_facing_child = Layer::Create(); + root->AddChild(front_facing_child); + auto back_facing_child = Layer::Create(); + root->AddChild(back_facing_child); + auto front_facing_surface = Layer::Create(); + root->AddChild(front_facing_surface); + auto back_facing_surface = Layer::Create(); + root->AddChild(back_facing_surface); + auto front_facing_child_of_front_facing_surface = Layer::Create(); + front_facing_surface->AddChild(front_facing_child_of_front_facing_surface); + auto back_facing_child_of_front_facing_surface = Layer::Create(); + front_facing_surface->AddChild(back_facing_child_of_front_facing_surface); + auto front_facing_child_of_back_facing_surface = Layer::Create(); + back_facing_surface->AddChild(front_facing_child_of_back_facing_surface); + auto back_facing_child_of_back_facing_surface = Layer::Create(); + back_facing_surface->AddChild(back_facing_child_of_back_facing_surface); + + // Nothing is double-sided + front_facing_child->SetDoubleSided(false); + back_facing_child->SetDoubleSided(false); + front_facing_surface->SetDoubleSided(false); + back_facing_surface->SetDoubleSided(false); + front_facing_child_of_front_facing_surface->SetDoubleSided(false); + back_facing_child_of_front_facing_surface->SetDoubleSided(false); + front_facing_child_of_back_facing_surface->SetDoubleSided(false); + back_facing_child_of_back_facing_surface->SetDoubleSided(false); + + // Everything draws content. + front_facing_child->SetIsDrawable(true); + back_facing_child->SetIsDrawable(true); + front_facing_surface->SetIsDrawable(true); + back_facing_surface->SetIsDrawable(true); + front_facing_child_of_front_facing_surface->SetIsDrawable(true); + back_facing_child_of_front_facing_surface->SetIsDrawable(true); + front_facing_child_of_back_facing_surface->SetIsDrawable(true); + back_facing_child_of_back_facing_surface->SetIsDrawable(true); + + gfx::Transform backface_matrix; + backface_matrix.Translate(50.0, 50.0); + backface_matrix.RotateAboutYAxis(180.0); + backface_matrix.Translate(-50.0, -50.0); + + root->SetBounds(gfx::Size(100, 100)); + front_facing_child->SetBounds(gfx::Size(100, 100)); + back_facing_child->SetBounds(gfx::Size(100, 100)); + front_facing_surface->SetBounds(gfx::Size(100, 100)); + back_facing_surface->SetBounds(gfx::Size(100, 100)); + front_facing_child_of_front_facing_surface->SetBounds(gfx::Size(100, 100)); + back_facing_child_of_front_facing_surface->SetBounds(gfx::Size(100, 100)); + front_facing_child_of_back_facing_surface->SetBounds(gfx::Size(100, 100)); + back_facing_child_of_back_facing_surface->SetBounds(gfx::Size(100, 100)); + + front_facing_surface->SetForceRenderSurfaceForTesting(true); + back_facing_surface->SetForceRenderSurfaceForTesting(true); + + back_facing_child->SetTransform(backface_matrix); + back_facing_surface->SetTransform(backface_matrix); + back_facing_child_of_front_facing_surface->SetTransform(backface_matrix); + back_facing_child_of_back_facing_surface->SetTransform(backface_matrix); + + // Note: No layers preserve 3d. According to current W3C CSS gfx::Transforms + // spec, these layers should blindly use their own local transforms to + // determine back-face culling. + CommitAndActivate(); + + // Verify which render surfaces were created. + EXPECT_EQ(GetRenderSurfaceImpl(front_facing_child), + GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child), + GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(front_facing_surface), + GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(back_facing_surface), + GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(back_facing_surface), + GetRenderSurfaceImpl(front_facing_surface)); + EXPECT_EQ(GetRenderSurfaceImpl(front_facing_child_of_front_facing_surface), + GetRenderSurfaceImpl(front_facing_surface)); + EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child_of_front_facing_surface), + GetRenderSurfaceImpl(front_facing_surface)); + EXPECT_EQ(GetRenderSurfaceImpl(front_facing_child_of_back_facing_surface), + GetRenderSurfaceImpl(back_facing_surface)); + EXPECT_EQ(GetRenderSurfaceImpl(back_facing_child_of_back_facing_surface), + GetRenderSurfaceImpl(back_facing_surface)); + + EXPECT_EQ(3u, update_layer_impl_list().size()); + EXPECT_TRUE(UpdateLayerImplListContains(front_facing_child->id())); + EXPECT_TRUE(UpdateLayerImplListContains(front_facing_surface->id())); + EXPECT_TRUE(UpdateLayerImplListContains( + front_facing_child_of_front_facing_surface->id())); +} + +// Verify that layers are appropriately culled when their back face is showing +// and they are not double sided, while animations are going on. +// Even layers that are animating get culled if their back face is showing and +// they are not double sided. +TEST_F(PropertyTreeBuilderTest, BackFaceCullingWithAnimatingTransforms) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto child = Layer::Create(); + root->AddChild(child); + auto animating_surface = Layer::Create(); + root->AddChild(animating_surface); + auto child_of_animating_surface = Layer::Create(); + animating_surface->AddChild(child_of_animating_surface); + auto animating_child = Layer::Create(); + root->AddChild(animating_child); + auto child2 = Layer::Create(); + root->AddChild(child2); + + // Nothing is double-sided + child->SetDoubleSided(false); + child2->SetDoubleSided(false); + animating_surface->SetDoubleSided(false); + child_of_animating_surface->SetDoubleSided(false); + animating_child->SetDoubleSided(false); + + // Everything draws content. + child->SetIsDrawable(true); + child2->SetIsDrawable(true); + animating_surface->SetIsDrawable(true); + child_of_animating_surface->SetIsDrawable(true); + animating_child->SetIsDrawable(true); + + gfx::Transform backface_matrix; + backface_matrix.Translate(50.0, 50.0); + backface_matrix.RotateAboutYAxis(180.0); + backface_matrix.Translate(-50.0, -50.0); + + host()->SetElementIdsForTesting(); + + // Animate the transform on the render surface. + AddAnimatedTransformToElementWithAnimation(animating_surface->element_id(), + timeline(), 10.0, 30, 0); + // This is just an animating layer, not a surface. + AddAnimatedTransformToElementWithAnimation(animating_child->element_id(), + timeline(), 10.0, 30, 0); + + root->SetBounds(gfx::Size(100, 100)); + child->SetBounds(gfx::Size(100, 100)); + child->SetTransform(backface_matrix); + animating_surface->SetBounds(gfx::Size(100, 100)); + animating_surface->SetTransform(backface_matrix); + animating_surface->SetForceRenderSurfaceForTesting(true); + child_of_animating_surface->SetBounds(gfx::Size(100, 100)); + child_of_animating_surface->SetTransform(backface_matrix); + animating_child->SetBounds(gfx::Size(100, 100)); + animating_child->SetTransform(backface_matrix); + child2->SetBounds(gfx::Size(100, 100)); + + CommitAndActivate(); + + EXPECT_EQ(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); + EXPECT_TRUE(GetRenderSurfaceImpl(animating_surface)); + EXPECT_EQ(GetRenderSurfaceImpl(child_of_animating_surface), + GetRenderSurfaceImpl(animating_surface)); + EXPECT_EQ(GetRenderSurfaceImpl(animating_child), GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(child2), GetRenderSurfaceImpl(root)); + + EXPECT_EQ(1u, update_layer_impl_list().size()); + + // The back facing layers are culled from the layer list, and have an empty + // visible rect. + EXPECT_TRUE(UpdateLayerImplListContains(child2->id())); + EXPECT_TRUE(ImplOf(child)->visible_layer_rect().IsEmpty()); + EXPECT_TRUE(ImplOf(animating_surface)->visible_layer_rect().IsEmpty()); + EXPECT_TRUE( + ImplOf(child_of_animating_surface)->visible_layer_rect().IsEmpty()); + EXPECT_TRUE(ImplOf(animating_child)->visible_layer_rect().IsEmpty()); + + EXPECT_EQ(gfx::Rect(100, 100), ImplOf(child2)->visible_layer_rect()); +} + +// Verify that having animated opacity but current opacity 1 still creates +// a render surface. +TEST_F(PropertyTreeBuilderTest, AnimatedOpacityCreatesRenderSurface) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto child = Layer::Create(); + root->AddChild(child); + auto grandchild = Layer::Create(); + child->AddChild(grandchild); + + root->SetBounds(gfx::Size(50, 50)); + child->SetBounds(gfx::Size(50, 50)); + child->SetIsDrawable(true); + grandchild->SetBounds(gfx::Size(50, 50)); + grandchild->SetIsDrawable(true); + + host()->SetElementIdsForTesting(); + AddOpacityTransitionToElementWithAnimation(child->element_id(), timeline(), + 10.0, 1.f, 0.2f, false); + CommitAndActivate(); + + EXPECT_EQ(1.f, ImplOf(child)->Opacity()); + EXPECT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child)); +} + +static bool FilterIsAnimating(LayerImpl* layer) { + MutatorHost* host = layer->layer_tree_impl()->mutator_host(); + return host->IsAnimatingFilterProperty(layer->element_id(), + layer->GetElementTypeForAnimation()); +} + +// Verify that having an animated filter (but no current filter, as these +// are mutually exclusive) correctly creates a render surface. +TEST_F(PropertyTreeBuilderTest, AnimatedFilterCreatesRenderSurface) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto child = Layer::Create(); + root->AddChild(child); + auto grandchild = Layer::Create(); + child->AddChild(grandchild); + + root->SetBounds(gfx::Size(50, 50)); + child->SetBounds(gfx::Size(50, 50)); + grandchild->SetBounds(gfx::Size(50, 50)); + + host()->SetElementIdsForTesting(); + AddAnimatedFilterToElementWithAnimation(child->element_id(), timeline(), 10.0, + 0.1f, 0.2f); + CommitAndActivate(); + + EXPECT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child)); + + EXPECT_TRUE(GetRenderSurfaceImpl(root)->Filters().IsEmpty()); + EXPECT_TRUE(GetRenderSurfaceImpl(child)->Filters().IsEmpty()); + + EXPECT_FALSE(FilterIsAnimating(ImplOf(root))); + EXPECT_TRUE(FilterIsAnimating(ImplOf(child))); + EXPECT_FALSE(FilterIsAnimating(ImplOf(grandchild))); +} + +bool HasPotentiallyRunningFilterAnimation(const LayerImpl& layer) { + MutatorHost* host = layer.layer_tree_impl()->mutator_host(); + return host->HasPotentiallyRunningFilterAnimation( + layer.element_id(), layer.GetElementTypeForAnimation()); +} + +// Verify that having a filter animation with a delayed start time creates a +// render surface. +TEST_F(PropertyTreeBuilderTest, DelayedFilterAnimationCreatesRenderSurface) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto child = Layer::Create(); + root->AddChild(child); + auto grandchild = Layer::Create(); + child->AddChild(grandchild); + + root->SetBounds(gfx::Size(50, 50)); + child->SetBounds(gfx::Size(50, 50)); + grandchild->SetBounds(gfx::Size(50, 50)); + + host()->SetElementIdsForTesting(); + + std::unique_ptr<KeyframedFilterAnimationCurve> curve( + KeyframedFilterAnimationCurve::Create()); + FilterOperations start_filters; + start_filters.Append(FilterOperation::CreateBrightnessFilter(0.1f)); + FilterOperations end_filters; + end_filters.Append(FilterOperation::CreateBrightnessFilter(0.3f)); + curve->AddKeyframe( + FilterKeyframe::Create(base::TimeDelta(), start_filters, nullptr)); + curve->AddKeyframe(FilterKeyframe::Create( + base::TimeDelta::FromMilliseconds(100), end_filters, nullptr)); + std::unique_ptr<KeyframeModel> keyframe_model = + KeyframeModel::Create(std::move(curve), 0, 1, TargetProperty::FILTER); + keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE); + keyframe_model->set_time_offset(base::TimeDelta::FromMilliseconds(-1000)); + + AddKeyframeModelToElementWithAnimation(child->element_id(), timeline(), + std::move(keyframe_model)); + CommitAndActivate(); + + EXPECT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_NE(GetRenderSurfaceImpl(child), GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(grandchild), GetRenderSurfaceImpl(child)); + + EXPECT_TRUE(GetRenderSurfaceImpl(root)->Filters().IsEmpty()); + EXPECT_TRUE(GetRenderSurfaceImpl(child)->Filters().IsEmpty()); + + EXPECT_FALSE(FilterIsAnimating(ImplOf(root))); + EXPECT_FALSE(HasPotentiallyRunningFilterAnimation(*ImplOf(root))); + EXPECT_FALSE(FilterIsAnimating(ImplOf(child))); + EXPECT_TRUE(HasPotentiallyRunningFilterAnimation(*ImplOf(child))); + EXPECT_FALSE(FilterIsAnimating(ImplOf(grandchild))); + EXPECT_FALSE(HasPotentiallyRunningFilterAnimation(*ImplOf(grandchild))); +} + +TEST_F(PropertyTreeBuilderTest, ChangingAxisAlignmentTriggersRebuild) { + gfx::Transform translate; + gfx::Transform rotate; + + translate.Translate(10, 10); + rotate.Rotate(45); + + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(800, 800)); + + host()->SetRootLayer(root); + + UpdateMainDrawProperties(); + EXPECT_FALSE(host()->property_trees()->needs_rebuild); + + root->SetTransform(translate); + EXPECT_FALSE(host()->property_trees()->needs_rebuild); + + root->SetTransform(rotate); + EXPECT_TRUE(host()->property_trees()->needs_rebuild); +} + +TEST_F(PropertyTreeBuilderTest, ResetPropertyTreeIndices) { + scoped_refptr<Layer> root = Layer::Create(); + root->SetBounds(gfx::Size(800, 800)); + + gfx::Transform translate_z; + translate_z.Translate3d(0, 0, 10); + + scoped_refptr<Layer> child = Layer::Create(); + child->SetTransform(translate_z); + child->SetBounds(gfx::Size(100, 100)); + + root->AddChild(child); + + host()->SetRootLayer(root); + + UpdateMainDrawProperties(); + EXPECT_NE(-1, child->transform_tree_index()); + + child->RemoveFromParent(); + + UpdateMainDrawProperties(); + EXPECT_EQ(-1, child->transform_tree_index()); +} + +TEST_F(PropertyTreeBuilderTest, RenderSurfaceClipsSubtree) { + // Ensure that a Clip Node is added when a render surface applies clip. + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto significant_transform = Layer::Create(); + root->AddChild(significant_transform); + auto layer_clips_subtree = Layer::Create(); + significant_transform->AddChild(layer_clips_subtree); + auto render_surface = Layer::Create(); + layer_clips_subtree->AddChild(render_surface); + auto test_layer = Layer::Create(); + render_surface->AddChild(test_layer); + + // This transform should be a significant one so that a transform node is + // formed for it. + gfx::Transform transform1; + transform1.RotateAboutYAxis(45); + transform1.RotateAboutXAxis(30); + // This transform should be a 3d transform as we want the render surface + // to flatten the transform + gfx::Transform transform2; + transform2.Translate3d(10, 10, 10); + + root->SetBounds(gfx::Size(30, 30)); + significant_transform->SetTransform(transform1); + significant_transform->SetBounds(gfx::Size(30, 30)); + layer_clips_subtree->SetBounds(gfx::Size(30, 30)); + layer_clips_subtree->SetMasksToBounds(true); + layer_clips_subtree->SetForceRenderSurfaceForTesting(true); + render_surface->SetTransform(transform2); + render_surface->SetBounds(gfx::Size(30, 30)); + render_surface->SetForceRenderSurfaceForTesting(true); + test_layer->SetBounds(gfx::Size(30, 30)); + test_layer->SetIsDrawable(true); + + CommitAndActivate(); + + EXPECT_TRUE(GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(significant_transform), + GetRenderSurfaceImpl(root)); + EXPECT_TRUE(GetRenderSurfaceImpl(layer_clips_subtree)); + EXPECT_NE(GetRenderSurfaceImpl(render_surface), GetRenderSurfaceImpl(root)); + EXPECT_EQ(GetRenderSurfaceImpl(test_layer), + GetRenderSurfaceImpl(render_surface)); + + EXPECT_EQ(gfx::Rect(30, 20), ImplOf(test_layer)->visible_layer_rect()); +} + +TEST_F(PropertyTreeBuilderTest, PropertyTreesRebuildWithOpacityChanges) { + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> child = Layer::Create(); + child->SetIsDrawable(true); + root->AddChild(child); + host()->SetRootLayer(root); + + root->SetBounds(gfx::Size(100, 100)); + child->SetBounds(gfx::Size(20, 20)); + UpdateMainDrawProperties(); + + // Changing the opacity from 1 to non-1 value should trigger rebuild of + // property trees as a new effect node will be created. + child->SetOpacity(0.5f); + PropertyTrees* property_trees = host()->property_trees(); + EXPECT_TRUE(property_trees->needs_rebuild); + + UpdateMainDrawProperties(); + EXPECT_NE(child->effect_tree_index(), root->effect_tree_index()); + + // child already has an effect node. Changing its opacity shouldn't trigger + // a property trees rebuild. + child->SetOpacity(0.8f); + property_trees = host()->property_trees(); + EXPECT_FALSE(property_trees->needs_rebuild); + + UpdateMainDrawProperties(); + EXPECT_NE(child->effect_tree_index(), root->effect_tree_index()); + + // Changing the opacity from non-1 value to 1 should trigger a rebuild of + // property trees as the effect node may no longer be needed. + child->SetOpacity(1.f); + property_trees = host()->property_trees(); + EXPECT_TRUE(property_trees->needs_rebuild); + + UpdateMainDrawProperties(); + EXPECT_EQ(child->effect_tree_index(), root->effect_tree_index()); +} + +TEST_F(PropertyTreeBuilderTest, RenderSurfaceListForTrilinearFiltering) { + auto root = Layer::Create(); + host()->SetRootLayer(root); + auto parent = Layer::Create(); + root->AddChild(parent); + auto child1 = Layer::Create(); + parent->AddChild(child1); + auto child2 = Layer::Create(); + parent->AddChild(child2); + + gfx::Transform scale_matrix; + scale_matrix.Scale(.25f, .25f); + + root->SetBounds(gfx::Size(200, 200)); + parent->SetTransform(scale_matrix); + parent->SetTrilinearFiltering(true); + child1->SetBounds(gfx::Size(50, 50)); + child1->SetIsDrawable(true); + child1->SetForceRenderSurfaceForTesting(true); + child2->SetPosition(gfx::PointF(50, 50)); + child2->SetBounds(gfx::Size(50, 50)); + child2->SetIsDrawable(true); + child2->SetForceRenderSurfaceForTesting(true); + + CommitAndActivate(); + + ASSERT_TRUE(GetRenderSurfaceImpl(parent)); + EXPECT_EQ(2, GetRenderSurfaceImpl(parent)->num_contributors()); + EXPECT_EQ(4U, GetRenderSurfaceList().size()); + + // The rectangle enclosing child1 and child2 (0,0 100x100), scaled by the + // scale matrix to (0,0 25x25). + EXPECT_EQ(gfx::RectF(0, 0, 25, 25), + GetRenderSurfaceImpl(parent)->DrawableContentRect()); +} + +TEST_F(PropertyTreeBuilderTest, RoundedCornerBounds) { + // Layer Tree: + // +root + // +--render surface + // +----rounded corner layer 1 [should trigger render surface] + // +----layer 1 + // +--rounded corner layer 2 [should trigger render surface] + // +----layer 2 + // +------rounded corner layer 3 [should trigger render surface] + // +--------rounded corner layer 4 [should trigger render surface] + + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + constexpr int kRoundedCorner3Radius = 1; + constexpr int kRoundedCorner4Radius = 1; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(15.f, 15.f, 20.f, 20.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f); + constexpr gfx::RectF kRoundedCornerLayer3Bound(0.f, 15.f, 5.f, 5.f); + constexpr gfx::RectF kRoundedCornerLayer4Bound(1.f, 1.f, 3.f, 3.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> render_surface = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> layer_1 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); + scoped_refptr<Layer> layer_2 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create(); + + // Set up layer tree + root->AddChild(render_surface); + root->AddChild(rounded_corner_layer_2); + + render_surface->AddChild(rounded_corner_layer_1); + render_surface->AddChild(layer_1); + + rounded_corner_layer_2->AddChild(layer_2); + + layer_2->AddChild(rounded_corner_layer_3); + + rounded_corner_layer_3->AddChild(rounded_corner_layer_4); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + render_surface->SetPosition(gfx::PointF(0, 0)); + rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); + layer_1->SetPosition(gfx::PointF(10.f, 10.f)); + rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); + layer_2->SetPosition(gfx::PointF(30.f, 30.f)); + rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin()); + rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin()); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + render_surface->SetBounds(gfx::Size(50, 50)); + rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + layer_1->SetBounds(gfx::Size(10, 10)); + rounded_corner_layer_2->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + layer_2->SetBounds(gfx::Size(25, 25)); + rounded_corner_layer_3->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); + rounded_corner_layer_4->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); + + // Add Layer transforms. + gfx::Transform layer_2_transform; + constexpr gfx::Vector2dF kLayer2Translation(10.f, 10.f); + layer_2_transform.Translate(kLayer2Translation); + layer_2->SetTransform(layer_2_transform); + + gfx::Transform rounded_corner_layer_3_transform; + constexpr float kRoundedCorner3Scale = 2.f; + rounded_corner_layer_3_transform.Scale(kRoundedCorner3Scale, + kRoundedCorner3Scale); + rounded_corner_layer_3->SetTransform(rounded_corner_layer_3_transform); + + // Set the layer properties + render_surface->SetForceRenderSurfaceForTesting(true); + + root->SetIsDrawable(true); + render_surface->SetIsDrawable(true); + rounded_corner_layer_1->SetIsDrawable(true); + layer_1->SetIsDrawable(true); + rounded_corner_layer_2->SetIsDrawable(true); + layer_2->SetIsDrawable(true); + rounded_corner_layer_3->SetIsDrawable(true); + rounded_corner_layer_4->SetIsDrawable(true); + + // Set Rounded corners + rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + rounded_corner_layer_2->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + rounded_corner_layer_3->SetRoundedCorner( + {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, + kRoundedCorner3Radius}); + rounded_corner_layer_4->SetRoundedCorner( + {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, + kRoundedCorner4Radius}); + + UpdateMainDrawProperties(kDeviceScale); + + // Since this effect node has no descendants that draw and no descendant that + // has a rounded corner, it does not need a render surface. + const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + // Since this node has descendants with roudned corners, it needs a render + // surface. It also has 2 descendants that draw. + effect_node = GetEffectNode(rounded_corner_layer_2.get()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + // Since this node has a descendant that has a rounded corner, it will trigger + // the creation of a render surface. + effect_node = GetEffectNode(rounded_corner_layer_3.get()); + gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), + kRoundedCorner3Radius); + EXPECT_EQ(rounded_corner_bounds_3.rect(), + gfx::RectF(kRoundedCornerLayer3Bound.size())); + + // Since this node has no descendants that draw nor any descendant that has a + // rounded corner, it does not need a render surface. + effect_node = GetEffectNode(rounded_corner_layer_4.get()); + gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), + kRoundedCorner4Radius); + EXPECT_EQ(rounded_corner_bounds_4.rect(), + gfx::RectF(kRoundedCornerLayer4Bound.size())); + + CommitAndActivate(kDeviceScale); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* rounded_corner_layer_1_impl = + layer_tree_impl->LayerById(rounded_corner_layer_1->id()); + LayerImpl* rounded_corner_layer_2_impl = + layer_tree_impl->LayerById(rounded_corner_layer_2->id()); + LayerImpl* rounded_corner_layer_3_impl = + layer_tree_impl->LayerById(rounded_corner_layer_3->id()); + LayerImpl* rounded_corner_layer_4_impl = + layer_tree_impl->LayerById(rounded_corner_layer_4->id()); + + EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); + + // Rounded corner layer 1 + // The render target for this layer is |render_surface|, hence its target + // bounds are relative to |render_surface|. + // The offset from the origin of the render target is [15, 15] and the device + // scale factor is 1.6 thus giving the target space origin of [24, 24]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_rrect_1 = + rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds; + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Rounded corner layer 2 + // The render target for this layer is |root|. + // The offset from the origin of the render target is [40, 40] and the device + // scale factor is 1.6 thus giving the target space origin of [64, 64]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_2 = + rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_2.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_2 = + rounded_corner_layer_2_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); + + // Rounded corner layer 3 + // The render target for this layer is |rounded_corner_2|. + // The net offset from the origin of the render target is [40, 55] and the + // device scale factor is 1.6 thus giving the target space origin of [64, 88]. + // The corner radius is also scaled by a factor of 1.6 * transform scale. + const gfx::RRectF actual_self_rrect_3 = + rounded_corner_layer_3_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer3Bound; + bounds_in_target_space += + layer_2->position().OffsetFromOrigin() + kLayer2Translation; + bounds_in_target_space.Scale(kDeviceScale); + gfx::SizeF transformed_size = bounds_in_target_space.size(); + transformed_size.Scale(kRoundedCorner3Scale); + bounds_in_target_space.set_size(transformed_size); + + const gfx::RRectF actual_render_target_rrect_3 = + rounded_corner_layer_3_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), + kRoundedCorner3Radius * kDeviceScale * kRoundedCorner3Scale); + + // Rounded corner layer 4 + // The render target for this layer is |rounded_corner_3|. + // The net offset from the origin of the render target is [1, 1] and the + // net scale is 1.6 * transform scale = 3.2 thus giving the target space o + // rigin of [3.2, 3.2]. + // The corner radius is also scaled by a factor of 3.2. + const gfx::RRectF actual_rrect_4 = + rounded_corner_layer_4_impl->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer4Bound; + bounds_in_target_space.Scale(kDeviceScale * kRoundedCorner3Scale); + EXPECT_EQ(actual_rrect_4.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_rrect_4.GetSimpleRadius(), + kRoundedCorner4Radius * kDeviceScale * kRoundedCorner3Scale); +} + +TEST_F(PropertyTreeBuilderTest, RoundedCornerBoundsInterveningRenderTarget) { + // Layer Tree: + // +root + // +--rounded corner layer 1 [should not trigger render surface] + // +----render surface [Does not draw] + // +------rounded corner layer 2 [should not trigger render surface] + + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(60.f, 0.f, 40.f, 30.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 30.f, 20.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> render_surface = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); + + // Set up layer tree + root->AddChild(rounded_corner_layer_1); + rounded_corner_layer_1->AddChild(render_surface); + render_surface->AddChild(rounded_corner_layer_2); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); + render_surface->SetPosition(gfx::PointF(0, 0)); + rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + render_surface->SetBounds(gfx::Size(30, 30)); + rounded_corner_layer_2->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + + // Set the layer properties + render_surface->SetForceRenderSurfaceForTesting(true); + + root->SetIsDrawable(true); + rounded_corner_layer_1->SetIsDrawable(true); + rounded_corner_layer_2->SetIsDrawable(true); + + // Set Rounded corners + rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + rounded_corner_layer_2->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + + UpdateMainDrawProperties(kDeviceScale); + + // Since this effect node has only 1 descendant that draws and no descendant + // that has a rounded corner before the render surface, it does not need a + // render surface. + const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + // Since this effect node has no descendants that draw and no descendant that + // has a rounded corner, it does not need a render surface. + effect_node = GetEffectNode(rounded_corner_layer_2.get()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + CommitAndActivate(kDeviceScale); + LayerTreeImpl* layer_tree_impl = host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* rounded_corner_layer_1_impl = + layer_tree_impl->LayerById(rounded_corner_layer_1->id()); + LayerImpl* rounded_corner_layer_2_impl = + layer_tree_impl->LayerById(rounded_corner_layer_2->id()); + + EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); + + // Rounded corner layer 1 + // The render target for this layer is |root|, hence its target + // bounds are relative to |root|. + // The offset from the origin of the render target is [60, 0] and the device + // scale factor is 1.6 thus giving the target space origin of [96, 0]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_rrect_1 = + rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds; + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Rounded corner layer 2 + // The render target for this layer is |render_surface|. + // The offset from the origin of the render target is [0, 0]. + const gfx::RRectF actual_rrect_2 = + rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); +} + +TEST_F(PropertyTreeBuilderTest, RoundedCornerBoundsSiblingRenderTarget) { + // Layer Tree: + // +root + // +--rounded corner layer 1 [should trigger render surface] + // +----render surface [Does not draw] + // +----rounded corner layer 2 [should not trigger render surface] + + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(0.f, 60.f, 30.f, 40.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 20.f, 30.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> render_surface = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); + + // Set up layer tree + root->AddChild(rounded_corner_layer_1); + rounded_corner_layer_1->AddChild(render_surface); + rounded_corner_layer_1->AddChild(rounded_corner_layer_2); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); + render_surface->SetPosition(gfx::PointF(0, 0)); + rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + render_surface->SetBounds(gfx::Size(30, 30)); + rounded_corner_layer_2->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + + // Set the layer properties + render_surface->SetForceRenderSurfaceForTesting(true); + + root->SetIsDrawable(true); + rounded_corner_layer_1->SetIsDrawable(true); + rounded_corner_layer_2->SetIsDrawable(true); + + // Set Rounded corners + rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + rounded_corner_layer_2->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + + UpdateMainDrawProperties(kDeviceScale); + + // Since this effect node has 1 descendant with a rounded corner without a + // render surface along the chain, it need a render surface. + const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + // Since this effect node has no descendants that draw and no descendant that + // has a rounded corner, it does not need a render surface. + effect_node = GetEffectNode(rounded_corner_layer_2.get()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + CommitAndActivate(kDeviceScale); + LayerTreeImpl* layer_tree_impl = host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* rounded_corner_layer_1_impl = + layer_tree_impl->LayerById(rounded_corner_layer_1->id()); + LayerImpl* rounded_corner_layer_2_impl = + layer_tree_impl->LayerById(rounded_corner_layer_2->id()); + + EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); + + // Rounded corner layer 1 + // The render target for this layer is |root|, hence its target + // bounds are relative to |root|. + // The offset from the origin of the render target is [0, 60] and the device + // scale factor is 1.6 thus giving the target space origin of [0, 96]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_1 = + rounded_corner_layer_1_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); + + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_1 = + rounded_corner_layer_1_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Rounded corner layer 2 + // The render target for this layer is |render_surface|. + // The offset from the origin of the render target is [0, 0]. + const gfx::RRectF actual_rrect_2 = + rounded_corner_layer_2_impl->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); +} + +TEST_F(PropertyTreeBuilderTest, FastRoundedCornerDoesNotTriggerRenderSurface) { + // Layer Tree: + // +root + // +--fast rounded corner layer [should not trigger render surface] + // +----layer 1 + // +----layer 2 + // +--rounded corner layer [should trigger render surface] + // +----layer 3 + // +----layer 4 + + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(0.f, 0.f, 50.f, 50.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(40.f, 40.f, 60.f, 60.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> fast_rounded_corner_layer = Layer::Create(); + scoped_refptr<Layer> layer_1 = Layer::Create(); + scoped_refptr<Layer> layer_2 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer = Layer::Create(); + scoped_refptr<Layer> layer_3 = Layer::Create(); + scoped_refptr<Layer> layer_4 = Layer::Create(); + + // Set up layer tree + root->AddChild(fast_rounded_corner_layer); + root->AddChild(rounded_corner_layer); + + fast_rounded_corner_layer->AddChild(layer_1); + fast_rounded_corner_layer->AddChild(layer_2); + + rounded_corner_layer->AddChild(layer_3); + rounded_corner_layer->AddChild(layer_4); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + fast_rounded_corner_layer->SetPosition(kRoundedCornerLayer1Bound.origin()); + layer_1->SetPosition(gfx::PointF(0.f, 0.f)); + layer_2->SetPosition(gfx::PointF(25.f, 0.f)); + rounded_corner_layer->SetPosition(kRoundedCornerLayer2Bound.origin()); + layer_3->SetPosition(gfx::PointF(0.f, 0.f)); + layer_4->SetPosition(gfx::PointF(30.f, 0.f)); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + fast_rounded_corner_layer->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + layer_1->SetBounds(gfx::Size(25, 25)); + layer_2->SetBounds(gfx::Size(25, 25)); + rounded_corner_layer->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + layer_3->SetBounds(gfx::Size(30, 60)); + layer_4->SetBounds(gfx::Size(30, 60)); + + root->SetIsDrawable(true); + fast_rounded_corner_layer->SetIsDrawable(true); + layer_1->SetIsDrawable(true); + layer_2->SetIsDrawable(true); + rounded_corner_layer->SetIsDrawable(true); + layer_3->SetIsDrawable(true); + layer_4->SetIsDrawable(true); + + // Set Rounded corners + fast_rounded_corner_layer->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + rounded_corner_layer->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + + fast_rounded_corner_layer->SetIsFastRoundedCorner(true); + + UpdateMainDrawProperties(kDeviceScale); + + // Since this layer has a fast rounded corner, it should not have a render + // surface even though it has 2 layers in the subtree that draws content. + const EffectNode* effect_node = + GetEffectNode(fast_rounded_corner_layer.get()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_TRUE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + // Since this node has 2 descendants that draw, it will have a rounded corner. + effect_node = GetEffectNode(rounded_corner_layer.get()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + CommitAndActivate(kDeviceScale); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* fast_rounded_corner_layer_impl = + layer_tree_impl->LayerById(fast_rounded_corner_layer->id()); + LayerImpl* layer_1_impl = layer_tree_impl->LayerById(layer_1->id()); + LayerImpl* layer_2_impl = layer_tree_impl->LayerById(layer_2->id()); + LayerImpl* rounded_corner_layer_impl = + layer_tree_impl->LayerById(rounded_corner_layer->id()); + LayerImpl* layer_3_impl = layer_tree_impl->LayerById(layer_3->id()); + LayerImpl* layer_4_impl = layer_tree_impl->LayerById(layer_4->id()); + + EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); + + // Fast rounded corner layer. + // The render target for this layer is |root|, hence its target bounds are + // relative to |root|. + // The offset from the origin of the render target is [0, 0] and the device + // scale factor is 1.6. + const gfx::RRectF actual_rrect_1 = + fast_rounded_corner_layer_impl->draw_properties().rounded_corner_bounds; + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Layer 1 and layer 2 rounded corner bounds + // This should have the same rounded corner boudns as fast rounded corner + // layer. + const gfx::RRectF layer_1_rrect = + layer_1_impl->draw_properties().rounded_corner_bounds; + const gfx::RRectF layer_2_rrect = + layer_2_impl->draw_properties().rounded_corner_bounds; + EXPECT_EQ(actual_rrect_1, layer_1_rrect); + EXPECT_EQ(actual_rrect_1, layer_2_rrect); + + // Rounded corner layer + // The render target for this layer is |root|. + // The offset from the origin of the render target is [40, 40] and the device + // scale factor is 1.6 thus giving the target space origin of [64, 64]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_2 = + rounded_corner_layer_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_2.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_2 = + rounded_corner_layer_impl->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); + + // Layer 3 and layer 4 should have no rounded corner bounds set as their + // parent is a render surface. + const gfx::RRectF layer_3_rrect = + layer_3_impl->draw_properties().rounded_corner_bounds; + const gfx::RRectF layer_4_rrect = + layer_4_impl->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(layer_3_rrect.IsEmpty()); + EXPECT_TRUE(layer_4_rrect.IsEmpty()); +} + +TEST_F(PropertyTreeBuilderTest, + FastRoundedCornerTriggersRenderSurfaceInAncestor) { + // Layer Tree: + // +root + // +--rounded corner layer [1] [should trigger render surface] + // +----fast rounded corner layer [2] [should not trigger render surface] + // +--rounded corner layer [3] [should trigger render surface] + // +----rounded corner layer [4] [should not trigger render surface] + + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + constexpr int kRoundedCorner3Radius = 1; + constexpr int kRoundedCorner4Radius = 3; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(5.f, 5.f, 50.f, 50.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(0.f, 0.f, 25.f, 25.f); + constexpr gfx::RectF kRoundedCornerLayer3Bound(40.f, 40.f, 60.f, 60.f); + constexpr gfx::RectF kRoundedCornerLayer4Bound(30.f, 0.f, 30.f, 60.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> fast_rounded_corner_layer_2 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_4 = Layer::Create(); + + // Set up layer tree + root->AddChild(rounded_corner_layer_1); + root->AddChild(rounded_corner_layer_3); + + rounded_corner_layer_1->AddChild(fast_rounded_corner_layer_2); + + rounded_corner_layer_3->AddChild(rounded_corner_layer_4); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); + fast_rounded_corner_layer_2->SetPosition(kRoundedCornerLayer2Bound.origin()); + rounded_corner_layer_3->SetPosition(kRoundedCornerLayer3Bound.origin()); + rounded_corner_layer_4->SetPosition(kRoundedCornerLayer4Bound.origin()); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + fast_rounded_corner_layer_2->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + rounded_corner_layer_3->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); + rounded_corner_layer_4->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); + + root->SetIsDrawable(true); + rounded_corner_layer_1->SetIsDrawable(true); + fast_rounded_corner_layer_2->SetIsDrawable(true); + rounded_corner_layer_3->SetIsDrawable(true); + rounded_corner_layer_4->SetIsDrawable(true); + + // Set Rounded corners + rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + fast_rounded_corner_layer_2->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + rounded_corner_layer_3->SetRoundedCorner( + {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, + kRoundedCorner3Radius}); + rounded_corner_layer_4->SetRoundedCorner( + {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, + kRoundedCorner4Radius}); + + fast_rounded_corner_layer_2->SetIsFastRoundedCorner(true); + + UpdateMainDrawProperties(kDeviceScale); + + // Since this layer has a descendant that has rounded corner, this node will + // require a render surface. + const EffectNode* effect_node = GetEffectNode(rounded_corner_layer_1.get()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + // Since this layer has no descendant with rounded corner or drawable, it will + // not have a render surface. + effect_node = GetEffectNode(fast_rounded_corner_layer_2.get()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_TRUE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + // Since this layer has 1 descendant with a rounded corner, it should have a + // render surface. + effect_node = GetEffectNode(rounded_corner_layer_3.get()); + gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), + kRoundedCorner3Radius); + EXPECT_EQ(rounded_corner_bounds_3.rect(), + gfx::RectF(kRoundedCornerLayer3Bound.size())); + + // Since this layer no descendants, it would no thave a render pass. + effect_node = GetEffectNode(rounded_corner_layer_4.get()); + gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), + kRoundedCorner4Radius); + EXPECT_EQ(rounded_corner_bounds_4.rect(), + gfx::RectF(kRoundedCornerLayer4Bound.size())); + + CommitAndActivate(kDeviceScale); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* rounded_corner_layer_impl_1 = + layer_tree_impl->LayerById(rounded_corner_layer_1->id()); + LayerImpl* fast_rounded_corner_layer_impl_2 = + layer_tree_impl->LayerById(fast_rounded_corner_layer_2->id()); + LayerImpl* rounded_corner_layer_impl_3 = + layer_tree_impl->LayerById(rounded_corner_layer_3->id()); + LayerImpl* rounded_corner_layer_impl_4 = + layer_tree_impl->LayerById(rounded_corner_layer_4->id()); + + EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); + + // Rounded corner layer 1. + // The render target for this layer is |root|, hence its target bounds are + // relative to |root|. + // The offset from the origin of the render target is [5, 5] and the device + // scale factor is 1.6 giving a total offset of [8, 8]. + const gfx::RRectF actual_self_rrect_1 = + rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); + + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_1 = + rounded_corner_layer_impl_1->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Fast rounded corner layer 2 + // The render target for this layer is |rounded_corner_layer_1|. + // The offset from the origin of the render target is [0, 0] and the device + // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_2 = + fast_rounded_corner_layer_impl_2->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_self_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); + + // Rounded corner layer 3 + // The render target for this layer is |root|. + // The offset from the origin of the render target is [40, 40] and the device + // scale factor is 1.6 thus giving the target space origin of [64, 64]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_3 = + rounded_corner_layer_impl_3->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer3Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_3 = + rounded_corner_layer_impl_3->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), + kRoundedCorner3Radius * kDeviceScale); + + // Rounded corner layer 4 + // The render target for this layer is |rounded_corner_layer_3|. + // The offset from the origin of the render target is [30, 0] and the device + // scale factor is 1.6 thus giving the target space origin of [48, 0]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_4 = + rounded_corner_layer_impl_4->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer4Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_self_rrect_4.GetSimpleRadius(), + kRoundedCorner4Radius * kDeviceScale); +} + +TEST_F(PropertyTreeBuilderTest, + FastRoundedCornerDoesNotTriggerRenderSurfaceFromSubtree) { + // Layer Tree: + // +root + // +--fast rounded corner layer 1 [should trigger render surface] + // +----rounded corner layer 1 [should not trigger render surface] + // +--rounded corner layer 2 [should trigger render surface] + // +----rounded corner layer 3 [should not trigger render surface] + constexpr int kRoundedCorner1Radius = 2; + constexpr int kRoundedCorner2Radius = 5; + constexpr int kRoundedCorner3Radius = 4; + constexpr int kRoundedCorner4Radius = 5; + + constexpr gfx::RectF kRoundedCornerLayer1Bound(10.f, 5.f, 45.f, 50.f); + constexpr gfx::RectF kRoundedCornerLayer2Bound(5.f, 5.f, 20.f, 20.f); + constexpr gfx::RectF kRoundedCornerLayer3Bound(60.f, 5.f, 40.f, 25.f); + constexpr gfx::RectF kRoundedCornerLayer4Bound(0.f, 10.f, 10.f, 20.f); + + constexpr float kDeviceScale = 1.6f; + + scoped_refptr<Layer> root = Layer::Create(); + scoped_refptr<Layer> fast_rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_1 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_2 = Layer::Create(); + scoped_refptr<Layer> rounded_corner_layer_3 = Layer::Create(); + + // Set up layer tree + root->AddChild(fast_rounded_corner_layer_1); + root->AddChild(rounded_corner_layer_2); + + fast_rounded_corner_layer_1->AddChild(rounded_corner_layer_1); + rounded_corner_layer_2->AddChild(rounded_corner_layer_3); + + // Set the root layer on host. + host()->SetRootLayer(root); + + // Set layer positions. + fast_rounded_corner_layer_1->SetPosition(kRoundedCornerLayer1Bound.origin()); + rounded_corner_layer_1->SetPosition(kRoundedCornerLayer2Bound.origin()); + rounded_corner_layer_2->SetPosition(kRoundedCornerLayer3Bound.origin()); + rounded_corner_layer_3->SetPosition(kRoundedCornerLayer4Bound.origin()); + + // Set up layer bounds. + root->SetBounds(gfx::Size(100, 100)); + fast_rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer1Bound.size())); + rounded_corner_layer_1->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer2Bound.size())); + rounded_corner_layer_2->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer3Bound.size())); + rounded_corner_layer_3->SetBounds( + gfx::ToRoundedSize(kRoundedCornerLayer4Bound.size())); + + root->SetIsDrawable(true); + fast_rounded_corner_layer_1->SetIsDrawable(true); + rounded_corner_layer_1->SetIsDrawable(true); + rounded_corner_layer_2->SetIsDrawable(true); + rounded_corner_layer_3->SetIsDrawable(true); + + // Set Rounded corners + fast_rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner1Radius, kRoundedCorner1Radius, kRoundedCorner1Radius, + kRoundedCorner1Radius}); + rounded_corner_layer_1->SetRoundedCorner( + {kRoundedCorner2Radius, kRoundedCorner2Radius, kRoundedCorner2Radius, + kRoundedCorner2Radius}); + rounded_corner_layer_2->SetRoundedCorner( + {kRoundedCorner3Radius, kRoundedCorner3Radius, kRoundedCorner3Radius, + kRoundedCorner3Radius}); + rounded_corner_layer_3->SetRoundedCorner( + {kRoundedCorner4Radius, kRoundedCorner4Radius, kRoundedCorner4Radius, + kRoundedCorner4Radius}); + + fast_rounded_corner_layer_1->SetIsFastRoundedCorner(true); + + UpdateMainDrawProperties(kDeviceScale); + + // Since this layer has a descendant with rounded corner, it needs a render + // surface. + const EffectNode* effect_node = + GetEffectNode(fast_rounded_corner_layer_1.get()); + gfx::RRectF rounded_corner_bounds_1 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_TRUE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_1.GetSimpleRadius(), + kRoundedCorner1Radius); + EXPECT_EQ(rounded_corner_bounds_1.rect(), + gfx::RectF(kRoundedCornerLayer1Bound.size())); + + // Since this layer has no descendant with rounded corner or drawable, it will + // not have a render surface. + effect_node = GetEffectNode(rounded_corner_layer_1.get()); + gfx::RRectF rounded_corner_bounds_2 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_2.GetSimpleRadius(), + kRoundedCorner2Radius); + EXPECT_EQ(rounded_corner_bounds_2.rect(), + gfx::RectF(kRoundedCornerLayer2Bound.size())); + + // Since this layer has a descendant with rounded corner, it should have a + // render surface. + effect_node = GetEffectNode(rounded_corner_layer_2.get()); + gfx::RRectF rounded_corner_bounds_3 = effect_node->rounded_corner_bounds; + EXPECT_TRUE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_3.GetSimpleRadius(), + kRoundedCorner3Radius); + EXPECT_EQ(rounded_corner_bounds_3.rect(), + gfx::RectF(kRoundedCornerLayer3Bound.size())); + + // Since this layer has no descendant, it does not need a render surface. + effect_node = GetEffectNode(rounded_corner_layer_3.get()); + gfx::RRectF rounded_corner_bounds_4 = effect_node->rounded_corner_bounds; + EXPECT_FALSE(effect_node->HasRenderSurface()); + EXPECT_FALSE(effect_node->is_fast_rounded_corner); + EXPECT_FLOAT_EQ(rounded_corner_bounds_4.GetSimpleRadius(), + kRoundedCorner4Radius); + EXPECT_EQ(rounded_corner_bounds_4.rect(), + gfx::RectF(kRoundedCornerLayer4Bound.size())); + + CommitAndActivate(kDeviceScale); + LayerTreeImpl* layer_tree_impl = host()->host_impl()->active_tree(); + + // Get the layer impl for each Layer. + LayerImpl* fast_rounded_corner_layer_impl_1 = + layer_tree_impl->LayerById(fast_rounded_corner_layer_1->id()); + LayerImpl* rounded_corner_layer_impl_1 = + layer_tree_impl->LayerById(rounded_corner_layer_1->id()); + LayerImpl* rounded_corner_layer_impl_2 = + layer_tree_impl->LayerById(rounded_corner_layer_2->id()); + LayerImpl* rounded_corner_layer_impl_3 = + layer_tree_impl->LayerById(rounded_corner_layer_3->id()); + + EXPECT_EQ(kDeviceScale, layer_tree_impl->device_scale_factor()); + + // Fast rounded corner layer 1. + // The render target for this layer is |root|, hence its target bounds are + // relative to |root|. + // The offset from the origin of the render target is [5, 5] and the device + // scale factor is 1.6. + const gfx::RRectF actual_self_rrect_1 = + fast_rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_1.IsEmpty()); + + gfx::RectF bounds_in_target_space = kRoundedCornerLayer1Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_1 = + fast_rounded_corner_layer_impl_1->render_target() + ->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_1.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_1.GetSimpleRadius(), + kRoundedCorner1Radius * kDeviceScale); + + // Rounded corner layer 1 + // The render target for this layer is |fast_rounded_corner_layer_1|. + // The offset from the origin of the render target is [0, 0] and the device + // scale factor is 1.6. The corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_2 = + rounded_corner_layer_impl_1->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer2Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_self_rrect_2.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_self_rrect_2.GetSimpleRadius(), + kRoundedCorner2Radius * kDeviceScale); + + // Rounded corner layer 3 + // The render target for this layer is |root|. + // The offset from the origin of the render target is [5, 5] and the device + // scale factor is 1.6 thus giving the target space origin of [8, 8]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_3 = + rounded_corner_layer_impl_2->draw_properties().rounded_corner_bounds; + EXPECT_TRUE(actual_self_rrect_3.IsEmpty()); + + bounds_in_target_space = kRoundedCornerLayer3Bound; + bounds_in_target_space.Scale(kDeviceScale); + const gfx::RRectF actual_render_target_rrect_3 = + rounded_corner_layer_impl_2->render_target()->rounded_corner_bounds(); + EXPECT_EQ(actual_render_target_rrect_3.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_render_target_rrect_3.GetSimpleRadius(), + kRoundedCorner3Radius * kDeviceScale); + + // Rounded corner layer 4 + // The render target for this layer is |rounded_corner_layer_2|. + // The offset from the origin of the render target is [0, 5] and the device + // scale factor is 1.6 thus giving the target space origin of [0, 8]. The + // corner radius is also scaled by a factor of 1.6. + const gfx::RRectF actual_self_rrect_4 = + rounded_corner_layer_impl_3->draw_properties().rounded_corner_bounds; + bounds_in_target_space = kRoundedCornerLayer4Bound; + bounds_in_target_space.Scale(kDeviceScale); + EXPECT_EQ(actual_self_rrect_4.rect(), bounds_in_target_space); + EXPECT_FLOAT_EQ(actual_self_rrect_4.GetSimpleRadius(), + kRoundedCorner4Radius * kDeviceScale); +} + +} // namespace +} // namespace cc |