blob: 5b5f96f4476073f77a7a8c3a3b06fa6f7d87f832 [file] [log] [blame]
// 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 <memory>
#include <utility>
#include "cc/animation/filter_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/animation/keyframe/keyframed_animation_curve.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/geometry/test/geometry_util.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/geometry/vector2d_conversions.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));
}
LayerImpl* GetLayerImpl(const scoped_refptr<Layer>& layer) {
return host()->host_impl()->active_tree()->LayerById(layer->id());
}
// 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_impl()->pending_tree()->PullLayerTreePropertiesFrom(
*host()->GetPendingCommitState());
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::CompositorRenderPassId>(root->id()),
GetRenderSurfaceList().at(0)->render_pass_id());
EXPECT_EQ(gfx::Rect(), ImplOf(root)->visible_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().asM33(), PaintFlags::FilterQuality::kLow, 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(50, 40, 10, 20),
ImplOf(filter_child)->visible_layer_rect());
EXPECT_EQ(gfx::Rect(0, -10, 10, 20),
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().asM33(), PaintFlags::FilterQuality::kLow, 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(50, 40, 10, 20),
ImplOf(filter_child)->visible_layer_rect());
EXPECT_EQ(gfx::Rect(0, -30, 30, 60),
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_TRANSFORM_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 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->IsAnimatingProperty(layer->element_id(),
layer->GetElementTypeForAnimation(),
TargetProperty::FILTER);
}
// 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->HasPotentiallyRunningAnimationForProperty(
layer.element_id(), layer.GetElementTypeForAnimation(),
TargetProperty::FILTER);
}
// 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::Milliseconds(100), end_filters, nullptr));
std::unique_ptr<KeyframeModel> keyframe_model = KeyframeModel::Create(
std::move(curve), 0, 1,
KeyframeModel::TargetPropertyId(TargetProperty::FILTER));
keyframe_model->set_fill_mode(KeyframeModel::FillMode::NONE);
keyframe_model->set_time_offset(base::Milliseconds(-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, GradientMask) {
auto root = Layer::Create();
host()->SetRootLayer(root);
root->SetBounds(gfx::Size(200, 200));
root->SetIsDrawable(true);
auto child1 = Layer::Create();
root->AddChild(child1);
child1->SetBounds(gfx::Size(100, 100));
child1->SetIsDrawable(true);
gfx::LinearGradient gradient_mask(45);
gradient_mask.AddStep(50, 0x50);
child1->SetGradientMask(gradient_mask);
// Without render surface.
CommitAndActivate();
{
auto* effect_node1 = GetEffectNode(child1.get());
EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners());
EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds());
EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask());
EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask());
EXPECT_FALSE(effect_node1->HasRenderSurface());
auto* layer_impl1 = GetLayerImpl(child1);
EXPECT_TRUE(
layer_impl1->draw_properties().mask_filter_info.HasGradientMask());
}
// Scale and translate should work.
gfx::Transform scale_and_translate_transform;
scale_and_translate_transform.Translate({10.f, 10.f});
scale_and_translate_transform.Scale(3.f, 2.f);
child1->SetTransform(scale_and_translate_transform);
CommitAndActivate();
{
// |mask_info| is in the coordinate space of the transform node associated
// with this effect node.
auto* effect_node1 = GetEffectNode(child1.get());
EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners());
EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask());
EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds());
EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask());
EXPECT_FALSE(effect_node1->HasRenderSurface());
// |mask_info| coordinates are in the target space of the layer.
auto* layer_impl1 = GetLayerImpl(child1);
EXPECT_FALSE(layer_impl1->draw_properties().mask_filter_info.IsEmpty());
EXPECT_FALSE(
layer_impl1->draw_properties().mask_filter_info.HasRoundedCorners());
EXPECT_TRUE(
layer_impl1->draw_properties().mask_filter_info.HasGradientMask());
EXPECT_EQ(gfx::RectF(10, 10, 300, 200),
layer_impl1->draw_properties().mask_filter_info.bounds());
// |angle| is updated by the scale transform.
EXPECT_EQ(33, layer_impl1->draw_properties()
.mask_filter_info.gradient_mask()
.angle());
EXPECT_EQ(gradient_mask.steps(), layer_impl1->draw_properties()
.mask_filter_info.gradient_mask()
.steps());
}
// Rotate transform eliminates gradient mask.
gfx::Transform rotate_transform;
rotate_transform.Rotate(45);
child1->SetTransform(rotate_transform);
CommitAndActivate();
{
auto* layer_impl1 = GetLayerImpl(child1);
EXPECT_EQ(gfx::RRectF::Type::kEmpty,
layer_impl1->draw_properties()
.mask_filter_info.rounded_corner_bounds()
.GetType());
EXPECT_FALSE(
layer_impl1->draw_properties().mask_filter_info.HasGradientMask());
}
// Reset transform
child1->SetTransform(gfx::Transform());
// A child layer will create a render surface.
auto grand_child1 = Layer::Create();
child1->AddChild(grand_child1);
grand_child1->SetBounds(gfx::Size(100, 100));
grand_child1->SetIsDrawable(true);
CommitAndActivate();
EXPECT_TRUE(GetEffectNode(child1.get())->HasRenderSurface());
{
auto* effect_node1 = GetEffectNode(child1.get());
EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask());
EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners());
EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds());
EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask());
EXPECT_TRUE(effect_node1->HasRenderSurface());
auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
EXPECT_FALSE(render_surface_impl1->mask_filter_info().IsEmpty());
EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasRoundedCorners());
EXPECT_TRUE(render_surface_impl1->mask_filter_info().HasGradientMask());
EXPECT_EQ(gfx::RectF(100, 100),
render_surface_impl1->mask_filter_info().bounds());
EXPECT_EQ(gradient_mask,
render_surface_impl1->mask_filter_info().gradient_mask());
}
child1->SetTransform(scale_and_translate_transform);
CommitAndActivate();
{
// |mask_info| is in the coordinate space of the transform node associated
// with this effect node.
auto* effect_node1 = GetEffectNode(child1.get());
EXPECT_TRUE(effect_node1->mask_filter_info.HasGradientMask());
EXPECT_FALSE(effect_node1->mask_filter_info.HasRoundedCorners());
EXPECT_EQ(gfx::RectF(100, 100), effect_node1->mask_filter_info.bounds());
EXPECT_EQ(gradient_mask, effect_node1->mask_filter_info.gradient_mask());
EXPECT_TRUE(effect_node1->HasRenderSurface());
// |mask_info| coordinates are in the target space of the render surface's
// layer.
auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
EXPECT_FALSE(render_surface_impl1->mask_filter_info().IsEmpty());
EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasRoundedCorners());
EXPECT_TRUE(render_surface_impl1->mask_filter_info().HasGradientMask());
EXPECT_EQ(gfx::RectF(10, 10, 300, 200),
render_surface_impl1->mask_filter_info().bounds());
// |angle| is updated by the scale transform.
EXPECT_EQ(33,
render_surface_impl1->mask_filter_info().gradient_mask().angle());
EXPECT_EQ(gradient_mask.steps(),
render_surface_impl1->mask_filter_info().gradient_mask().steps());
}
// Rotate transform eliminates gradient mask.
child1->SetTransform(rotate_transform);
CommitAndActivate();
{
auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
EXPECT_EQ(gfx::RRectF::Type::kEmpty,
render_surface_impl1->mask_filter_info()
.rounded_corner_bounds()
.GetType());
EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasGradientMask());
}
}
TEST_F(PropertyTreeBuilderTest, NestedGradientMask) {
auto root = Layer::Create();
host()->SetRootLayer(root);
root->SetBounds(gfx::Size(200, 200));
root->SetIsDrawable(true);
auto child1 = Layer::Create();
root->AddChild(child1);
child1->SetBounds(gfx::Size(100, 100));
child1->SetIsDrawable(true);
auto grand_child1 = Layer::Create();
child1->AddChild(grand_child1);
grand_child1->SetBounds(gfx::Size(50, 50));
grand_child1->SetIsDrawable(true);
gfx::LinearGradient gradient_mask1(30);
gradient_mask1.AddStep(50, 0x50);
child1->SetGradientMask(gradient_mask1);
gfx::LinearGradient gradient_mask2(45);
gradient_mask2.AddStep(0, 0xFF);
gradient_mask2.AddStep(100, 0x0);
grand_child1->SetGradientMask(gradient_mask2);
CommitAndActivate();
EXPECT_TRUE(GetEffectNode(child1.get())->HasRenderSurface());
{
auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
EXPECT_EQ(gradient_mask1,
render_surface_impl1->mask_filter_info().gradient_mask());
auto* effect_node2 = GetEffectNode(grand_child1.get());
EXPECT_FALSE(effect_node2->mask_filter_info.IsEmpty());
EXPECT_FALSE(effect_node2->mask_filter_info.HasRoundedCorners());
EXPECT_TRUE(effect_node2->mask_filter_info.HasGradientMask());
EXPECT_EQ(gfx::RectF(50, 50), effect_node2->mask_filter_info.bounds());
EXPECT_EQ(gradient_mask2, effect_node2->mask_filter_info.gradient_mask());
EXPECT_FALSE(effect_node2->HasRenderSurface());
auto& draw_properties2 = GetLayerImpl(grand_child1)->draw_properties();
EXPECT_FALSE(draw_properties2.mask_filter_info.IsEmpty());
EXPECT_FALSE(draw_properties2.mask_filter_info.HasRoundedCorners());
EXPECT_TRUE(draw_properties2.mask_filter_info.HasGradientMask());
EXPECT_EQ(gfx::RectF(50, 50), draw_properties2.mask_filter_info.bounds());
EXPECT_EQ(gradient_mask2,
draw_properties2.mask_filter_info.gradient_mask());
}
gfx::Transform scale_and_translate_transform1;
scale_and_translate_transform1.Translate({10.f, 10.f});
scale_and_translate_transform1.Scale(3.f, 2.f);
child1->SetTransform(scale_and_translate_transform1);
gfx::Transform scale_and_translate_transform2;
scale_and_translate_transform2.Translate({10.f, 5.f});
scale_and_translate_transform2.Scale(2.f, 1.5f);
grand_child1->SetTransform(scale_and_translate_transform2);
CommitAndActivate();
EXPECT_TRUE(GetEffectNode(child1.get())->HasRenderSurface());
{
// |mask_info| coordinates are in the target space of the render surface's
// layer.
auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
EXPECT_EQ(gradient_mask1.steps(),
render_surface_impl1->mask_filter_info().gradient_mask().steps());
// |angle| is updated by the scale transform.
EXPECT_EQ(21,
render_surface_impl1->mask_filter_info().gradient_mask().angle());
// |mask_info| is in the coordinate space of the transform node associated
// with this effect node.
auto* effect_node2 = GetEffectNode(grand_child1.get());
EXPECT_FALSE(effect_node2->HasRenderSurface());
// |mask_info| coordinates are in the target space of the layer.
auto& draw_properties2 = GetLayerImpl(grand_child1)->draw_properties();
EXPECT_FALSE(draw_properties2.mask_filter_info.IsEmpty());
EXPECT_FALSE(draw_properties2.mask_filter_info.HasRoundedCorners());
EXPECT_TRUE(draw_properties2.mask_filter_info.HasGradientMask());
EXPECT_EQ(gfx::RectF(30, 10, 300, 150),
draw_properties2.mask_filter_info.bounds());
// |angle| is updated by the scale transform.
EXPECT_EQ(26, draw_properties2.mask_filter_info.gradient_mask().angle());
EXPECT_EQ(gradient_mask2.steps(),
draw_properties2.mask_filter_info.gradient_mask().steps());
}
gfx::Transform rotate_transform;
rotate_transform.Rotate(45);
child1->SetTransform(rotate_transform);
CommitAndActivate();
{
auto* render_surface_impl1 = GetRenderSurfaceImpl(child1);
EXPECT_EQ(gfx::RRectF::Type::kEmpty,
render_surface_impl1->mask_filter_info()
.rounded_corner_bounds()
.GetType());
EXPECT_FALSE(render_surface_impl1->mask_filter_info().HasGradientMask());
}
}
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->mask_filter_info.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->mask_filter_info.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->mask_filter_info.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->mask_filter_info.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()
.mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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->mask_filter_info.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->mask_filter_info.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()
.mask_filter_info.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()
.mask_filter_info.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->mask_filter_info.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->mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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->mask_filter_info.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->mask_filter_info.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()
.mask_filter_info.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().mask_filter_info.rounded_corner_bounds();
const gfx::RRectF layer_2_rrect =
layer_2_impl->draw_properties().mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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().mask_filter_info.rounded_corner_bounds();
const gfx::RRectF layer_4_rrect =
layer_4_impl->draw_properties().mask_filter_info.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->mask_filter_info.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->mask_filter_info.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->mask_filter_info.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->mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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->mask_filter_info.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->mask_filter_info.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->mask_filter_info.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->mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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()
.mask_filter_info.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()
->mask_filter_info()
.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()
.mask_filter_info.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, SubtreeSize) {
constexpr viz::SubtreeCaptureId kCaptureId{42};
auto parent = Layer::Create();
host()->SetRootLayer(parent);
auto child = Layer::Create();
parent->AddChild(child);
child->SetSubtreeCaptureId(kCaptureId);
// Layer has empty bounds.
Commit(1.1f);
EffectNode* node = GetEffectNode(child.get());
EXPECT_EQ((gfx::Size{}), node->subtree_size);
EXPECT_EQ(kCaptureId, node->subtree_capture_id);
// Layer has bounds, scaling is 1.
child->SetBounds(gfx::Size{1280, 720});
Commit(1.0f);
node = GetEffectNode(child.get());
EXPECT_EQ((gfx::Size{1280, 720}), node->subtree_size);
// Layer has bounds, scaling is 2.
Commit(2.0f);
node = GetEffectNode(child.get());
EXPECT_EQ((gfx::Size{2560, 1440}), node->subtree_size);
}
} // namespace
} // namespace cc