summaryrefslogtreecommitdiffstats
path: root/chromium/cc/trees/property_tree_builder_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc/trees/property_tree_builder_unittest.cc')
-rw-r--r--chromium/cc/trees/property_tree_builder_unittest.cc1954
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