Add cc::MirrorLayer[Impl]
This new type of layer can be used to mirror contents of another layer.
When a mirror layer is set to mirror another layer, the latter is forced
to create a render pass so that the former can add a RenderPassDrawQuad
referring to that render pass, mirroring its contents.
Note that this CL does not add damage rect handling for the mirror
layer. That would be done in a follow-up CL.
The first use case for the mirror layer would be creating docked
magnifier on CrOS. The current implementation of docked magnifier does
not work with OOP-D.
BUG=947565
Change-Id: I1609edf53eb8c1ae8faff5345ee41dc547a916a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1652202
Reviewed-by: enne <enne@chromium.org>
Commit-Queue: Mohsen Izadi <mohsen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672744}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 25e01d1..d5d3bac 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -88,6 +88,10 @@
"layers/layer_position_constraint.h",
"layers/layer_sticky_position_constraint.cc",
"layers/layer_sticky_position_constraint.h",
+ "layers/mirror_layer.cc",
+ "layers/mirror_layer.h",
+ "layers/mirror_layer_impl.cc",
+ "layers/mirror_layer_impl.h",
"layers/nine_patch_generator.cc",
"layers/nine_patch_generator.h",
"layers/nine_patch_layer.cc",
@@ -605,6 +609,7 @@
"layers/layer_list_iterator_unittest.cc",
"layers/layer_position_constraint_unittest.cc",
"layers/layer_unittest.cc",
+ "layers/mirror_layer_unittest.cc",
"layers/nine_patch_generator_unittest.cc",
"layers/nine_patch_layer_impl_unittest.cc",
"layers/nine_patch_layer_unittest.cc",
@@ -681,6 +686,7 @@
"trees/layer_tree_host_pixeltest_blending.cc",
"trees/layer_tree_host_pixeltest_filters.cc",
"trees/layer_tree_host_pixeltest_masks.cc",
+ "trees/layer_tree_host_pixeltest_mirror.cc",
"trees/layer_tree_host_pixeltest_readback.cc",
"trees/layer_tree_host_pixeltest_scrollbars.cc",
"trees/layer_tree_host_pixeltest_synchronous.cc",
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 35f58477c..fc8629c 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -71,7 +71,8 @@
has_will_change_transform_hint(false),
trilinear_filtering(false),
hide_layer_and_subtree(false),
- overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto) {}
+ overscroll_behavior(OverscrollBehavior::kOverscrollBehaviorTypeAuto),
+ mirror_count(0) {}
Layer::Inputs::~Inputs() = default;
@@ -1500,6 +1501,7 @@
layer->SetMasksToBounds(inputs_.masks_to_bounds);
layer->SetNonFastScrollableRegion(inputs_.non_fast_scrollable_region);
layer->SetTouchActionRegion(inputs_.touch_action_region);
+ layer->SetMirrorCount(inputs_.mirror_count);
// TODO(sunxd): Pass the correct region for wheel event handlers, see
// https://crbug.com/841364.
EventListenerProperties mouse_wheel_props =
@@ -1693,6 +1695,27 @@
SetNeedsCommit();
}
+void Layer::IncrementMirrorCount() {
+ SetMirrorCount(mirror_count() + 1);
+}
+
+void Layer::DecrementMirrorCount() {
+ SetMirrorCount(mirror_count() - 1);
+}
+
+void Layer::SetMirrorCount(int mirror_count) {
+ if (inputs_.mirror_count == mirror_count)
+ return;
+
+ DCHECK_LE(0, mirror_count);
+ bool was_mirrored = inputs_.mirror_count > 0;
+ inputs_.mirror_count = mirror_count;
+ bool is_mirrored = inputs_.mirror_count > 0;
+ if (was_mirrored != is_mirrored)
+ SetPropertyTreesNeedRebuild();
+ SetNeedsPushProperties();
+}
+
ElementListType Layer::GetElementTypeForAnimation() const {
return ElementListType::ACTIVE;
}
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 58631ace3..fd5c08a 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -651,6 +651,11 @@
void SetTrilinearFiltering(bool trilinear_filtering);
bool trilinear_filtering() const { return inputs_.trilinear_filtering; }
+ // Increments/decrements/gets number of layers mirroring this layer.
+ void IncrementMirrorCount();
+ void DecrementMirrorCount();
+ int mirror_count() const { return inputs_.mirror_count; }
+
// Called on the scroll layer to trigger showing the overlay scrollbars.
void ShowScrollbars() { needs_show_scrollbars_ = true; }
@@ -930,6 +935,8 @@
// they are marked as needing to be rebuilt.
void UpdateScrollOffset(const gfx::ScrollOffset&);
+ void SetMirrorCount(int mirror_count);
+
// Encapsulates all data, callbacks or interfaces received from the embedder.
struct Inputs {
explicit Inputs(int layer_id);
@@ -1048,6 +1055,8 @@
OverscrollBehavior overscroll_behavior;
base::Optional<SnapContainerData> snap_container_data;
+
+ int mirror_count;
};
Layer* parent_;
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 86ccf0fc..5acf62e 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -80,7 +80,8 @@
scrollbars_hidden_(false),
needs_show_scrollbars_(false),
raster_even_if_not_drawn_(false),
- has_transform_node_(false) {
+ has_transform_node_(false),
+ mirror_count_(0) {
DCHECK_GT(layer_id_, 0);
DCHECK(layer_tree_impl_);
@@ -345,6 +346,7 @@
layer->clip_tree_index_ = clip_tree_index_;
layer->scroll_tree_index_ = scroll_tree_index_;
layer->has_will_change_transform_hint_ = has_will_change_transform_hint_;
+ layer->mirror_count_ = mirror_count_;
layer->scrollbars_hidden_ = scrollbars_hidden_;
if (needs_show_scrollbars_)
layer->needs_show_scrollbars_ = needs_show_scrollbars_;
@@ -516,7 +518,6 @@
needs_push_properties_ = false;
update_rect_.SetRect(0, 0, 0, 0);
- damage_rect_.SetRect(0, 0, 0, 0);
}
bool LayerImpl::IsActive() const {
@@ -696,12 +697,16 @@
layer_tree_impl_->AddToElementLayerList(element_id_, this);
}
+void LayerImpl::SetMirrorCount(int mirror_count) {
+ mirror_count_ = mirror_count;
+}
+
void LayerImpl::SetUpdateRect(const gfx::Rect& update_rect) {
update_rect_ = update_rect;
}
-void LayerImpl::AddDamageRect(const gfx::Rect& damage_rect) {
- damage_rect_.Union(damage_rect);
+gfx::Rect LayerImpl::GetDamageRect() const {
+ return gfx::Rect();
}
void LayerImpl::SetCurrentScrollOffset(const gfx::ScrollOffset& scroll_offset) {
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index d669445..423eb7b 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -194,6 +194,9 @@
void SetElementId(ElementId element_id);
ElementId element_id() const { return element_id_; }
+ void SetMirrorCount(int mirror_count);
+ int mirror_count() const { return mirror_count_; }
+
bool IsAffectedByPageScale() const;
bool Is3dSorted() const { return GetSortingContextId() != 0; }
@@ -332,8 +335,10 @@
void SetUpdateRect(const gfx::Rect& update_rect);
const gfx::Rect& update_rect() const { return update_rect_; }
- void AddDamageRect(const gfx::Rect& damage_rect);
- const gfx::Rect& damage_rect() const { return damage_rect_; }
+ // Denotes an area that is damaged and needs redraw. This is in the layer's
+ // space. By default returns empty rect, but can be overridden by subclasses
+ // as appropriate.
+ virtual gfx::Rect GetDamageRect() const;
virtual std::unique_ptr<base::DictionaryValue> LayerAsJson() const;
// TODO(pdr): This should be removed because there is no longer a tree
@@ -349,7 +354,7 @@
// from property_trees changes in animaiton.
bool LayerPropertyChangedNotFromPropertyTrees() const;
- void ResetChangeTracking();
+ virtual void ResetChangeTracking();
virtual SimpleEnclosedRegion VisibleOpaqueRegion() const;
@@ -567,10 +572,6 @@
// This is in the layer's space.
gfx::Rect update_rect_;
- // Denotes an area that is damaged and needs redraw. This is in the layer's
- // space.
- gfx::Rect damage_rect_;
-
// Group of properties that need to be computed based on the layer tree
// hierarchy before layers can be drawn.
DrawProperties draw_properties_;
@@ -596,6 +597,10 @@
bool raster_even_if_not_drawn_ : 1;
bool has_transform_node_ : 1;
+
+ // Number of layers mirroring this layer. If greater than zero, forces a
+ // render pass for the layer so it can be embedded by the mirroring layer.
+ int mirror_count_;
};
} // namespace cc
diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc
index be4d26a..7eeeffc 100644
--- a/cc/layers/layer_unittest.cc
+++ b/cc/layers/layer_unittest.cc
@@ -1654,6 +1654,78 @@
test_layer->SetLayerTreeHost(nullptr);
}
+// Verifies that mirror count is pushed to the LayerImpl.
+TEST_F(LayerTest, MirrorCountIsPushed) {
+ scoped_refptr<Layer> test_layer = Layer::Create();
+ std::unique_ptr<LayerImpl> impl_layer =
+ LayerImpl::Create(host_impl_.active_tree(), 1);
+ test_layer->SetLayerTreeHost(layer_tree_host_.get());
+ EXPECT_EQ(0, test_layer->mirror_count());
+ EXPECT_EQ(0, impl_layer->mirror_count());
+
+ test_layer->IncrementMirrorCount();
+ EXPECT_EQ(1, test_layer->mirror_count());
+ EXPECT_EQ(0, impl_layer->mirror_count());
+
+ test_layer->PushPropertiesTo(impl_layer.get());
+ EXPECT_EQ(1, test_layer->mirror_count());
+ EXPECT_EQ(1, impl_layer->mirror_count());
+
+ test_layer->SetLayerTreeHost(nullptr);
+}
+
+// Verifies that when mirror count of the layer is incremented or decremented,
+// SetPropertyTreesNeedRebuild() and SetNeedsPushProperties() are called
+// appropriately.
+TEST_F(LayerTest, UpdateMirrorCount) {
+ scoped_refptr<Layer> test_layer = Layer::Create();
+ test_layer->SetLayerTreeHost(layer_tree_host_.get());
+ layer_tree_host_->property_trees()->needs_rebuild = false;
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+ EXPECT_EQ(0, test_layer->mirror_count());
+ EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_EQ(0u, layer_tree_host_->LayersThatShouldPushProperties().size());
+
+ // Incrementing mirror count from zero should trigger property trees rebuild.
+ test_layer->IncrementMirrorCount();
+ EXPECT_EQ(1, test_layer->mirror_count());
+ EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ test_layer.get()));
+
+ layer_tree_host_->property_trees()->needs_rebuild = false;
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+ // Incrementing mirror count from non-zero should not trigger property trees
+ // rebuild.
+ test_layer->IncrementMirrorCount();
+ EXPECT_EQ(2, test_layer->mirror_count());
+ EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ test_layer.get()));
+
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+ // Decrementing mirror count to non-zero should not trigger property trees
+ // rebuild.
+ test_layer->DecrementMirrorCount();
+ EXPECT_EQ(1, test_layer->mirror_count());
+ EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ test_layer.get()));
+
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+ // Decrementing mirror count to zero should trigger property trees rebuild.
+ test_layer->DecrementMirrorCount();
+ EXPECT_EQ(0, test_layer->mirror_count());
+ EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ test_layer.get()));
+
+ test_layer->SetLayerTreeHost(nullptr);
+}
+
class LayerTestWithLayerLists : public LayerTest {
protected:
void SetUp() override {
diff --git a/cc/layers/mirror_layer.cc b/cc/layers/mirror_layer.cc
new file mode 100644
index 0000000..b89e300d
--- /dev/null
+++ b/cc/layers/mirror_layer.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 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/layers/mirror_layer.h"
+
+#include "cc/layers/mirror_layer_impl.h"
+
+namespace cc {
+
+std::unique_ptr<LayerImpl> MirrorLayer::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return MirrorLayerImpl::Create(tree_impl, id());
+}
+
+void MirrorLayer::PushPropertiesTo(LayerImpl* layer) {
+ Layer::PushPropertiesTo(layer);
+
+ auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
+ mirror_layer->SetMirroredLayerId(mirrored_layer_->id());
+}
+
+void MirrorLayer::SetLayerTreeHost(LayerTreeHost* host) {
+#if DCHECK_IS_ON()
+ if (host && host != layer_tree_host()) {
+ for (auto* p = parent(); p; p = p->parent())
+ DCHECK_NE(p, mirrored_layer_.get());
+ }
+#endif
+
+ Layer::SetLayerTreeHost(host);
+}
+
+scoped_refptr<MirrorLayer> MirrorLayer::Create(
+ scoped_refptr<Layer> mirrored_layer) {
+ return base::WrapRefCounted(new MirrorLayer(std::move(mirrored_layer)));
+}
+
+MirrorLayer::MirrorLayer(scoped_refptr<Layer> mirrored_layer)
+ : mirrored_layer_(std::move(mirrored_layer)) {
+ DCHECK(mirrored_layer_);
+ mirrored_layer_->IncrementMirrorCount();
+}
+
+MirrorLayer::~MirrorLayer() {
+ mirrored_layer_->DecrementMirrorCount();
+}
+
+} // namespace cc
diff --git a/cc/layers/mirror_layer.h b/cc/layers/mirror_layer.h
new file mode 100644
index 0000000..9915dcf
--- /dev/null
+++ b/cc/layers/mirror_layer.h
@@ -0,0 +1,42 @@
+// Copyright 2019 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.
+
+#ifndef CC_LAYERS_MIRROR_LAYER_H_
+#define CC_LAYERS_MIRROR_LAYER_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer.h"
+
+namespace cc {
+
+// A layer that can mirror contents of another Layer.
+class CC_EXPORT MirrorLayer : public Layer {
+ public:
+ static scoped_refptr<MirrorLayer> Create(scoped_refptr<Layer> mirrored_layer);
+
+ MirrorLayer(const MirrorLayer&) = delete;
+ MirrorLayer& operator=(const MirrorLayer&) = delete;
+
+ Layer* mirrored_layer() const { return mirrored_layer_.get(); }
+
+ // Layer overrides.
+ std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+ void PushPropertiesTo(LayerImpl* layer) override;
+ void SetLayerTreeHost(LayerTreeHost* host) override;
+
+ protected:
+ explicit MirrorLayer(scoped_refptr<Layer> mirrored_layer);
+
+ private:
+ ~MirrorLayer() override;
+
+ // A reference to a layer that is mirrored by this layer. |mirrored_layer_|
+ // cannot be an ancestor of |this|.
+ scoped_refptr<Layer> mirrored_layer_;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_MIRROR_LAYER_H_
diff --git a/cc/layers/mirror_layer_impl.cc b/cc/layers/mirror_layer_impl.cc
new file mode 100644
index 0000000..00de04dc
--- /dev/null
+++ b/cc/layers/mirror_layer_impl.cc
@@ -0,0 +1,88 @@
+// Copyright 2019 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/layers/mirror_layer_impl.h"
+
+#include "cc/trees/effect_node.h"
+#include "cc/trees/layer_tree_impl.h"
+#include "cc/trees/occlusion.h"
+#include "components/viz/common/quads/render_pass_draw_quad.h"
+
+namespace cc {
+
+MirrorLayerImpl::MirrorLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id) {}
+
+MirrorLayerImpl::~MirrorLayerImpl() = default;
+
+std::unique_ptr<LayerImpl> MirrorLayerImpl::CreateLayerImpl(
+ LayerTreeImpl* tree_impl) {
+ return MirrorLayerImpl::Create(tree_impl, id());
+}
+
+void MirrorLayerImpl::AppendQuads(viz::RenderPass* render_pass,
+ AppendQuadsData* append_quads_data) {
+ // TODO(mohsen): Currently, effects on the mirrored layer (e.g mask and
+ // opacity) are ignored. Consider applying them here.
+
+ auto* mirrored_layer = layer_tree_impl()->LayerById(mirrored_layer_id_);
+ auto* mirrored_render_surface =
+ GetEffectTree().GetRenderSurface(mirrored_layer->effect_tree_index());
+ gfx::Rect content_rect = mirrored_render_surface->content_rect();
+
+ gfx::Rect unoccluded_content_rect =
+ draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
+ content_rect);
+ if (unoccluded_content_rect.IsEmpty())
+ return;
+
+ viz::SharedQuadState* shared_quad_state =
+ render_pass->CreateAndAppendSharedQuadState();
+ bool contents_opaque = false;
+ auto* effect_node = GetEffectTree().Node(effect_tree_index());
+ shared_quad_state->SetAll(
+ draw_properties().target_space_transform, content_rect, content_rect,
+ draw_properties().rounded_corner_bounds, draw_properties().clip_rect,
+ draw_properties().is_clipped, contents_opaque, draw_properties().opacity,
+ effect_node->HasRenderSurface() ? SkBlendMode::kSrcOver
+ : effect_node->blend_mode,
+ GetSortingContextId());
+ shared_quad_state->is_fast_rounded_corner =
+ draw_properties().is_fast_rounded_corner;
+
+ AppendDebugBorderQuad(render_pass, content_rect, shared_quad_state,
+ append_quads_data);
+
+ viz::ResourceId mask_resource_id = 0;
+ gfx::RectF mask_uv_rect;
+ gfx::Size mask_texture_size;
+
+ auto* mirrored_effect_node = mirrored_render_surface->OwningEffectNode();
+ auto* quad = render_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
+ quad->SetNew(shared_quad_state, content_rect, unoccluded_content_rect,
+ mirrored_layer_id_, mask_resource_id, mask_uv_rect,
+ mask_texture_size, mirrored_effect_node->surface_contents_scale,
+ mirrored_effect_node->filters_origin,
+ gfx::RectF(gfx::Rect(content_rect.size())),
+ !layer_tree_impl()->settings().enable_edge_anti_aliasing, 0.f);
+}
+
+void MirrorLayerImpl::PushPropertiesTo(LayerImpl* layer) {
+ LayerImpl::PushPropertiesTo(layer);
+
+ auto* mirror_layer = static_cast<MirrorLayerImpl*>(layer);
+ mirror_layer->SetMirroredLayerId(mirrored_layer_id_);
+}
+
+gfx::Rect MirrorLayerImpl::GetDamageRect() const {
+ // TOOD(mohsen): Currently, the whole layer is marked as damaged. We should
+ // only consider the damage from the mirrored layer.
+ return gfx::Rect(bounds());
+}
+
+const char* MirrorLayerImpl::LayerTypeAsString() const {
+ return "cc::MirrorLayerImpl";
+}
+
+} // namespace cc
diff --git a/cc/layers/mirror_layer_impl.h b/cc/layers/mirror_layer_impl.h
new file mode 100644
index 0000000..0ab1a270
--- /dev/null
+++ b/cc/layers/mirror_layer_impl.h
@@ -0,0 +1,62 @@
+// Copyright 2019 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.
+
+#ifndef CC_LAYERS_MIRROR_LAYER_IMPL_H_
+#define CC_LAYERS_MIRROR_LAYER_IMPL_H_
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "cc/cc_export.h"
+#include "cc/layers/layer_impl.h"
+
+namespace cc {
+
+// This type of layer is used to mirror contents of another layer (specified by
+// |mirrored_layer_id_|) by forcing a render pass for the mirrored layer and
+// adding a RenderPassDrawQuad in the compositor frame for this layer referring
+// to that render pass. The mirroring layer should not be a descendant of the
+// mirrored layer (in terms of the effect tree). Due to ordering requirements
+// for render passes in the compositor frame, the render pass containing
+// mirroring layer should appear after the render pass created for the mirrored
+// layer. Currently, render passes are in reverse-draw order of the effect tree,
+// so we should be careful that this reverse-draw order does not conflict with
+// render pass ordering requirement mentioned above.
+// TODO(mohsen): If necessary, reorder render passes in compositor frame such
+// that the render pass containing mirroring layer appears after the render pass
+// created for the mirrored layer.
+class CC_EXPORT MirrorLayerImpl : public LayerImpl {
+ public:
+ static std::unique_ptr<MirrorLayerImpl> Create(LayerTreeImpl* tree_impl,
+ int id) {
+ return base::WrapUnique(new MirrorLayerImpl(tree_impl, id));
+ }
+
+ MirrorLayerImpl(const MirrorLayerImpl&) = delete;
+ MirrorLayerImpl& operator=(const MirrorLayerImpl&) = delete;
+
+ ~MirrorLayerImpl() override;
+
+ void SetMirroredLayerId(int id) { mirrored_layer_id_ = id; }
+ int mirrored_layer_id() const { return mirrored_layer_id_; }
+
+ // LayerImpl overrides.
+ std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
+ void AppendQuads(viz::RenderPass* render_pass,
+ AppendQuadsData* append_quads_data) override;
+ void PushPropertiesTo(LayerImpl* layer) override;
+ gfx::Rect GetDamageRect() const override;
+
+ protected:
+ MirrorLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ private:
+ const char* LayerTypeAsString() const override;
+
+ int mirrored_layer_id_ = 0;
+};
+
+} // namespace cc
+
+#endif // CC_LAYERS_MIRROR_LAYER_IMPL_H_
diff --git a/cc/layers/mirror_layer_unittest.cc b/cc/layers/mirror_layer_unittest.cc
new file mode 100644
index 0000000..9630d80
--- /dev/null
+++ b/cc/layers/mirror_layer_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2019 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 <memory>
+
+#include "cc/animation/animation_host.h"
+#include "cc/layers/mirror_layer.h"
+#include "cc/layers/mirror_layer_impl.h"
+#include "cc/test/fake_impl_task_runner_provider.h"
+#include "cc/test/fake_layer_tree_host.h"
+#include "cc/test/fake_layer_tree_host_client.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/test_task_graph_runner.h"
+#include "cc/trees/tree_synchronizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+class MirrorLayerTest : public testing::Test {
+ public:
+ MirrorLayerTest() : host_impl_(&task_runner_provider_, &task_graph_runner_) {}
+
+ // Synchronizes |layer_tree_host_| and |host_impl_| and pushes surface ids.
+ void SynchronizeTrees() {
+ TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(),
+ host_impl_.pending_tree());
+ }
+
+ protected:
+ void SetUp() override {
+ animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
+ layer_tree_host_ = FakeLayerTreeHost::Create(
+ &fake_client_, &task_graph_runner_, animation_host_.get());
+ layer_tree_host_->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f,
+ viz::LocalSurfaceIdAllocation());
+ host_impl_.CreatePendingTree();
+ }
+
+ void TearDown() override {
+ layer_tree_host_->SetRootLayer(nullptr);
+ layer_tree_host_ = nullptr;
+ }
+
+ FakeLayerTreeHostClient fake_client_;
+ FakeImplTaskRunnerProvider task_runner_provider_;
+ TestTaskGraphRunner task_graph_runner_;
+ std::unique_ptr<AnimationHost> animation_host_;
+ std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
+ FakeLayerTreeHostImpl host_impl_;
+};
+
+// This test verifies that MirrorLayer properties are pushed across to
+// MirrorLayerImpl.
+TEST_F(MirrorLayerTest, PushProperties) {
+ auto root = Layer::Create();
+ layer_tree_host_->SetRootLayer(root);
+
+ auto mirrored = Layer::Create();
+ root->AddChild(mirrored);
+ auto mirror = MirrorLayer::Create(mirrored);
+ root->AddChild(mirror);
+
+ EXPECT_EQ(1, mirrored->mirror_count());
+ EXPECT_EQ(mirrored.get(), mirror->mirrored_layer());
+
+ auto root_impl = LayerImpl::Create(host_impl_.pending_tree(), root->id());
+ auto mirrored_impl =
+ LayerImpl::Create(host_impl_.pending_tree(), mirrored->id());
+ auto mirror_impl =
+ MirrorLayerImpl::Create(host_impl_.pending_tree(), mirror->id());
+
+ // Verify that impl layers have default property values.
+ EXPECT_EQ(0, mirrored_impl->mirror_count());
+ EXPECT_EQ(0, mirror_impl->mirrored_layer_id());
+
+ SynchronizeTrees();
+
+ // Verify that property values are pushed to impl layers.
+ EXPECT_EQ(1, mirrored_impl->mirror_count());
+ EXPECT_EQ(mirrored_impl->id(), mirror_impl->mirrored_layer_id());
+}
+
+// This test verifies adding/removing mirror layers updates mirror count
+// properly and sets appropriate bits on the layer tree host.
+TEST_F(MirrorLayerTest, MirrorCount) {
+ auto mirrored = Layer::Create();
+ mirrored->SetLayerTreeHost(layer_tree_host_.get());
+
+ layer_tree_host_->property_trees()->needs_rebuild = false;
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+ EXPECT_EQ(0, mirrored->mirror_count());
+
+ // Creating the first mirror layer should trigger property trees rebuild.
+ auto mirror1 = MirrorLayer::Create(mirrored);
+ EXPECT_EQ(1, mirrored->mirror_count());
+ EXPECT_EQ(mirrored.get(), mirror1->mirrored_layer());
+ EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ mirrored.get()));
+
+ layer_tree_host_->property_trees()->needs_rebuild = false;
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+ // Creating a second mirror layer should not trigger property trees rebuild.
+ auto mirror2 = MirrorLayer::Create(mirrored);
+ EXPECT_EQ(2, mirrored->mirror_count());
+ EXPECT_EQ(mirrored.get(), mirror2->mirrored_layer());
+ EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ mirrored.get()));
+
+ layer_tree_host_->property_trees()->needs_rebuild = false;
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+ // Destroying one of the mirror layers should not trigger property trees
+ // rebuild.
+ mirror1->RemoveFromParent();
+ mirror1 = nullptr;
+ EXPECT_EQ(1, mirrored->mirror_count());
+ EXPECT_FALSE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_EQ(1u, layer_tree_host_->LayersThatShouldPushProperties().size());
+
+ layer_tree_host_->property_trees()->needs_rebuild = false;
+ layer_tree_host_->ClearLayersThatShouldPushProperties();
+
+ // Destroying the only remaining mirror layer should trigger property trees
+ // rebuild.
+ mirror2->RemoveFromParent();
+ mirror2 = nullptr;
+ EXPECT_EQ(0, mirrored->mirror_count());
+ EXPECT_TRUE(layer_tree_host_->property_trees()->needs_rebuild);
+ EXPECT_TRUE(base::Contains(layer_tree_host_->LayersThatShouldPushProperties(),
+ mirrored.get()));
+
+ mirrored->SetLayerTreeHost(nullptr);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 644a34b..fa04946c 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -764,7 +764,7 @@
void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) {
if (layer_tree_impl()->IsActiveTree())
- AddDamageRect(tile->enclosing_layer_rect());
+ damage_rect_.Union(tile->enclosing_layer_rect());
if (tile->draw_info().NeedsRaster()) {
PictureLayerTiling* tiling =
tilings_->FindTilingWithScaleKey(tile->contents_scale_key());
@@ -773,6 +773,15 @@
}
}
+gfx::Rect PictureLayerImpl::GetDamageRect() const {
+ return damage_rect_;
+}
+
+void PictureLayerImpl::ResetChangeTracking() {
+ LayerImpl::ResetChangeTracking();
+ damage_rect_.SetRect(0, 0, 0, 0);
+}
+
void PictureLayerImpl::DidBeginTracing() {
raster_source_->DidBeginTracing();
}
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 6d564e1..f122754 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -52,6 +52,8 @@
void AppendQuads(viz::RenderPass* render_pass,
AppendQuadsData* append_quads_data) override;
void NotifyTileStateChanged(const Tile* tile) override;
+ gfx::Rect GetDamageRect() const override;
+ void ResetChangeTracking() override;
void ResetRasterScale();
void DidBeginTracing() override;
void ReleaseResources() override;
@@ -242,6 +244,10 @@
gfx::Size content_bounds_;
TileSizeCalculator tile_size_calculator_;
+
+ // Denotes an area that is damaged and needs redraw. This is in the layer's
+ // space.
+ gfx::Rect damage_rect_;
};
} // namespace cc
diff --git a/cc/trees/damage_tracker.cc b/cc/trees/damage_tracker.cc
index f3ddee2..743b681 100644
--- a/cc/trees/damage_tracker.cc
+++ b/cc/trees/damage_tracker.cc
@@ -386,7 +386,7 @@
// If the layer properties haven't changed, then the the target surface is
// only affected by the layer's damaged area, which could be empty.
gfx::Rect damage_rect =
- gfx::UnionRects(layer->update_rect(), layer->damage_rect());
+ gfx::UnionRects(layer->update_rect(), layer->GetDamageRect());
damage_rect.Intersect(gfx::Rect(layer->bounds()));
if (!damage_rect.IsEmpty()) {
@@ -416,7 +416,7 @@
if (layer_is_new || !layer->update_rect().IsEmpty() ||
layer->LayerPropertyChangedNotFromPropertyTrees() ||
- !layer->damage_rect().IsEmpty() || property_change_on_non_target_node) {
+ !layer->GetDamageRect().IsEmpty() || property_change_on_non_target_node) {
has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty();
}
}
diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc
index fcd75ca..b9046c66 100644
--- a/cc/trees/damage_tracker_unittest.cc
+++ b/cc/trees/damage_tracker_unittest.cc
@@ -26,6 +26,36 @@
namespace cc {
namespace {
+class TestLayerImpl : public LayerImpl {
+ public:
+ TestLayerImpl(LayerTreeImpl* tree_impl, int id);
+
+ void AddDamageRect(const gfx::Rect& damage_rect);
+
+ // LayerImpl overrides.
+ gfx::Rect GetDamageRect() const override;
+ void ResetChangeTracking() override;
+
+ private:
+ gfx::Rect damage_rect_;
+};
+
+TestLayerImpl::TestLayerImpl(LayerTreeImpl* tree_impl, int id)
+ : LayerImpl(tree_impl, id) {}
+
+void TestLayerImpl::AddDamageRect(const gfx::Rect& damage_rect) {
+ damage_rect_.Union(damage_rect);
+}
+
+gfx::Rect TestLayerImpl::GetDamageRect() const {
+ return damage_rect_;
+}
+
+void TestLayerImpl::ResetChangeTracking() {
+ LayerImpl::ResetChangeTracking();
+ damage_rect_.SetRect(0, 0, 0, 0);
+}
+
void ExecuteCalculateDrawProperties(LayerImpl* root,
float device_scale_factor,
RenderSurfaceList* render_surface_list) {
@@ -71,16 +101,15 @@
LayerImpl* CreateTestTreeWithOneSurface(int number_of_children) {
host_impl_.active_tree()->DetachLayers();
- std::unique_ptr<LayerImpl> root =
- LayerImpl::Create(host_impl_.active_tree(), 1);
+ auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1);
root->SetBounds(gfx::Size(500, 500));
root->SetDrawsContent(true);
root->test_properties()->force_render_surface = true;
for (int i = 0; i < number_of_children; ++i) {
- std::unique_ptr<LayerImpl> child =
- LayerImpl::Create(host_impl_.active_tree(), 2 + i);
+ auto child =
+ std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2 + i);
child->test_properties()->position = gfx::PointF(100.f, 100.f);
child->SetBounds(gfx::Size(30, 30));
child->SetDrawsContent(true);
@@ -98,16 +127,13 @@
// two children of its own.
host_impl_.active_tree()->DetachLayers();
- std::unique_ptr<LayerImpl> root =
- LayerImpl::Create(host_impl_.active_tree(), 1);
- std::unique_ptr<LayerImpl> child1 =
- LayerImpl::Create(host_impl_.active_tree(), 2);
- std::unique_ptr<LayerImpl> child2 =
- LayerImpl::Create(host_impl_.active_tree(), 3);
- std::unique_ptr<LayerImpl> grand_child1 =
- LayerImpl::Create(host_impl_.active_tree(), 4);
- std::unique_ptr<LayerImpl> grand_child2 =
- LayerImpl::Create(host_impl_.active_tree(), 5);
+ auto root = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 1);
+ auto child1 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 2);
+ auto child2 = std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 3);
+ auto grand_child1 =
+ std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 4);
+ auto grand_child2 =
+ std::make_unique<TestLayerImpl>(host_impl_.active_tree(), 5);
root->SetBounds(gfx::Size(500, 500));
root->SetDrawsContent(true);
@@ -276,7 +302,8 @@
TEST_F(DamageTrackerTest, VerifyDamageForLayerDamageRects) {
LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
- LayerImpl* child = root->test_properties()->children[0];
+ auto* child =
+ static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
// CASE 1: Adding the layer damage rect should cause the corresponding damage
// to the surface.
@@ -336,7 +363,8 @@
TEST_F(DamageTrackerTest, VerifyDamageForLayerUpdateAndDamageRects) {
LayerImpl* root = CreateAndSetUpTestTreeWithOneSurface();
- LayerImpl* child = root->test_properties()->children[0];
+ auto* child =
+ static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
// CASE 1: Adding the layer damage rect and update rect should cause the
// corresponding damage to the surface.
@@ -547,9 +575,12 @@
TEST_F(DamageTrackerTest,
VerifyDamageForUpdateAndDamageRectsFromContributingContents) {
LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
- LayerImpl* child1 = root->test_properties()->children[0];
- LayerImpl* child2 = root->test_properties()->children[1];
- LayerImpl* grandchild1 = child1->test_properties()->children[0];
+ auto* child1 =
+ static_cast<TestLayerImpl*>(root->test_properties()->children[0]);
+ auto* child2 =
+ static_cast<TestLayerImpl*>(root->test_properties()->children[1]);
+ auto* grandchild1 =
+ static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
// CASE 1: Adding the layer1's damage rect and update rect should cause the
// corresponding damage to the surface.
@@ -1912,8 +1943,10 @@
TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurface) {
LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
LayerImpl* child1 = root->test_properties()->children[0];
- LayerImpl* grandchild1 = child1->test_properties()->children[0];
- LayerImpl* grandchild2 = child1->test_properties()->children[1];
+ auto* grandchild1 =
+ static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
+ auto* grandchild2 =
+ static_cast<TestLayerImpl*>(child1->test_properties()->children[1]);
// Really far left.
grandchild1->test_properties()->position =
@@ -2002,8 +2035,10 @@
TEST_F(DamageTrackerTest, DamageRectTooBigInRenderSurfaceWithFilter) {
LayerImpl* root = CreateAndSetUpTestTreeWithTwoSurfaces();
LayerImpl* child1 = root->test_properties()->children[0];
- LayerImpl* grandchild1 = child1->test_properties()->children[0];
- LayerImpl* grandchild2 = child1->test_properties()->children[1];
+ auto* grandchild1 =
+ static_cast<TestLayerImpl*>(child1->test_properties()->children[0]);
+ auto* grandchild2 =
+ static_cast<TestLayerImpl*>(child1->test_properties()->children[1]);
// Set up a moving pixels filter on the child.
FilterOperations filters;
diff --git a/cc/trees/effect_node.h b/cc/trees/effect_node.h
index 340305e..dd77eab 100644
--- a/cc/trees/effect_node.h
+++ b/cc/trees/effect_node.h
@@ -40,6 +40,7 @@
kTrilinearFiltering,
kCache,
kCopyRequest,
+ kMirrored,
// This must be the last value because it's used in tracing code to know the
// number of reasons.
kTest,
diff --git a/cc/trees/layer_tree_host_pixeltest_mirror.cc b/cc/trees/layer_tree_host_pixeltest_mirror.cc
new file mode 100644
index 0000000..ed011f43
--- /dev/null
+++ b/cc/trees/layer_tree_host_pixeltest_mirror.cc
@@ -0,0 +1,83 @@
+// Copyright 2019 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 "build/build_config.h"
+#include "cc/layers/mirror_layer.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/test/layer_tree_pixel_test.h"
+#include "cc/test/pixel_comparator.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform_util.h"
+
+#if !defined(OS_ANDROID)
+
+namespace cc {
+namespace {
+
+class LayerTreeHostMirrorPixelTest
+ : public LayerTreePixelTest,
+ public ::testing::WithParamInterface<LayerTreeTest::RendererType> {
+ protected:
+ RendererType renderer_type() { return GetParam(); }
+};
+
+const LayerTreeTest::RendererType kRendererTypes[] = {
+ LayerTreeTest::RENDERER_GL,
+ LayerTreeTest::RENDERER_SKIA_GL,
+ LayerTreeTest::RENDERER_SOFTWARE,
+#if defined(ENABLE_CC_VULKAN_TESTS)
+ LayerTreeTest::RENDERER_SKIA_VK,
+#endif
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ LayerTreeHostMirrorPixelTest,
+ ::testing::ValuesIn(kRendererTypes));
+
+// Verifies that a mirror layer with a scale mirrors another layer correctly.
+TEST_P(LayerTreeHostMirrorPixelTest, MirrorLayer) {
+ const float scale = 2.f;
+ gfx::Rect background_bounds(120, 180);
+ gfx::Rect mirrored_bounds(10, 10, 50, 50);
+ gfx::Rect mirror_bounds(10, 70, 100, 100);
+
+ auto background = CreateSolidColorLayer(background_bounds, SK_ColorWHITE);
+
+ auto mirrored_layer = CreateSolidColorLayerWithBorder(
+ mirrored_bounds, SK_ColorGREEN, 5, SK_ColorBLUE);
+
+ auto mirror_layer = MirrorLayer::Create(mirrored_layer);
+ mirror_layer->SetIsDrawable(true);
+ mirror_layer->SetBounds(mirror_bounds.size());
+ mirror_layer->SetPosition(gfx::PointF(mirror_bounds.origin()));
+ mirror_layer->SetTransform(gfx::GetScaleTransform(gfx::Point(), scale));
+ background->AddChild(mirrored_layer);
+ background->AddChild(mirror_layer);
+
+ if (renderer_type() == RENDERER_SOFTWARE) {
+ const bool discard_alpha = true;
+ const float error_pixels_percentage_limit = 3.f;
+ const float small_error_pixels_percentage_limit = 0.f;
+ const float avg_abs_error_limit = 65.f;
+ const int max_abs_error_limit = 120;
+ const int small_error_threshold = 0;
+ pixel_comparator_ = std::make_unique<FuzzyPixelComparator>(
+ discard_alpha, error_pixels_percentage_limit,
+ small_error_pixels_percentage_limit, avg_abs_error_limit,
+ max_abs_error_limit, small_error_threshold);
+ }
+
+#if defined(ENABLE_CC_VULKAN_TESTS) && defined(OS_LINUX)
+ if (renderer_type() == RENDERER_SKIA_VK)
+ pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(true);
+#endif
+
+ RunPixelTest(renderer_type(), background,
+ base::FilePath(FILE_PATH_LITERAL("mirror_layer.png")));
+}
+
+} // namespace
+} // namespace cc
+
+#endif // !defined(OS_ANDROID)
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc
index 7d20ffa..cf90575 100644
--- a/cc/trees/property_tree_builder.cc
+++ b/cc/trees/property_tree_builder.cc
@@ -867,6 +867,14 @@
return !layer->test_properties()->copy_requests.empty();
}
+static inline int MirrorCount(Layer* layer) {
+ return layer->mirror_count();
+}
+
+static inline int MirrorCount(LayerImpl* layer) {
+ return 0;
+}
+
static inline bool PropertyChanged(Layer* layer) {
return layer->subtree_property_changed();
}
@@ -979,6 +987,10 @@
if (HasCopyRequest(layer))
return RenderSurfaceReason::kCopyRequest;
+ // If the layer is mirrored.
+ if (MirrorCount(layer))
+ return RenderSurfaceReason::kMirrored;
+
return RenderSurfaceReason::kNone;
}
diff --git a/components/viz/test/data/mirror_layer.png b/components/viz/test/data/mirror_layer.png
new file mode 100644
index 0000000..4dc0edc9
--- /dev/null
+++ b/components/viz/test/data/mirror_layer.png
Binary files differ
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 3b95dd1a..d4dd1215 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -16,6 +16,7 @@
#include "base/memory/ptr_util.h"
#include "base/numerics/ranges.h"
#include "base/trace_event/trace_event.h"
+#include "cc/layers/mirror_layer.h"
#include "cc/layers/nine_patch_layer.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/solid_color_layer.h"
@@ -240,6 +241,24 @@
return mirror;
}
+void Layer::MirrorLayer(Layer* mirrored_layer) {
+ DCHECK_EQ(type_, LAYER_SOLID_COLOR);
+
+ if (!mirrored_layer) {
+ SetShowSolidColorContent();
+ return;
+ }
+
+ auto new_layer = cc::MirrorLayer::Create(mirrored_layer->cc_layer_);
+ SwitchToLayer(new_layer);
+ mirror_layer_ = new_layer;
+ gfx::Rect bounds = bounds_;
+ bounds.set_size(mirrored_layer->bounds().size());
+ SetBounds(bounds);
+
+ RecomputeDrawsContentAndUVRect();
+}
+
const Compositor* Layer::GetCompositor() const {
return GetRoot(this)->compositor_;
}
@@ -684,6 +703,7 @@
solid_color_layer_ = nullptr;
texture_layer_ = nullptr;
surface_layer_ = nullptr;
+ mirror_layer_ = nullptr;
for (auto* child : children_) {
DCHECK(child->cc_layer_);
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 9ffad84..98471b7 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -35,6 +35,7 @@
namespace cc {
class Layer;
+class MirrorLayer;
class NinePatchLayer;
class SolidColorLayer;
class SurfaceLayer;
@@ -82,6 +83,11 @@
// content is only mirrored if painted by a delegate or backed by a surface.
std::unique_ptr<Layer> Mirror();
+ // Sets up this layer to mirror contents of |mirrored_layer|. This layer
+ // should be of type |LAYER_SOLID_COLOR| and should not be a descendant of the
+ // |mirrored_layer|.
+ void MirrorLayer(Layer* mirrored_layer);
+
// This method is relevant only if this layer is a mirror destination layer.
// Sets whether this mirror layer's bounds are synchronized with the source
// layer's bounds.
@@ -437,6 +443,7 @@
bool FillsBoundsCompletely() const override;
size_t GetApproximateUnsharedMemoryUsage() const override;
+ cc::MirrorLayer* mirror_layer_for_testing() { return mirror_layer_.get(); }
cc::Layer* cc_layer_for_testing() { return cc_layer_; }
const cc::Layer* cc_layer_for_testing() const { return cc_layer_; }
@@ -652,6 +659,7 @@
// Ownership of the layer is held through one of the strongly typed layer
// pointers, depending on which sort of layer this is.
scoped_refptr<cc::PictureLayer> content_layer_;
+ scoped_refptr<cc::MirrorLayer> mirror_layer_;
scoped_refptr<cc::NinePatchLayer> nine_patch_layer_;
scoped_refptr<cc::TextureLayer> texture_layer_;
scoped_refptr<cc::SolidColorLayer> solid_color_layer_;
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 252f92a..c05d461 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -30,6 +30,7 @@
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/single_keyframe_effect_animation.h"
#include "cc/layers/layer.h"
+#include "cc/layers/mirror_layer.h"
#include "cc/test/pixel_test_utils.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
@@ -1511,6 +1512,69 @@
EXPECT_EQ(mask->damaged_region_for_testing().bounds(), gfx::Rect());
}
+// Verifies that when a layer is mirroring other layers, mirror count
+// of mirrored layers is updated properly.
+TEST_F(LayerWithNullDelegateTest, MirrorLayer) {
+ std::unique_ptr<Layer> mirrored_layer_1(CreateLayer(LAYER_SOLID_COLOR));
+ auto* mirrored_layer_1_cc = mirrored_layer_1->cc_layer_for_testing();
+
+ std::unique_ptr<Layer> mirrored_layer_2(CreateLayer(LAYER_SOLID_COLOR));
+ auto* mirrored_layer_2_cc = mirrored_layer_2->cc_layer_for_testing();
+
+ std::unique_ptr<Layer> mirror_layer(CreateLayer(LAYER_SOLID_COLOR));
+
+ // Originally, mirror counts should be zero.
+ auto* mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+ EXPECT_EQ(nullptr, mirror_layer_cc);
+ EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+ EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+
+ // Mirror the first layer. Its mirror count should be increased.
+ mirror_layer->MirrorLayer(mirrored_layer_1.get());
+ mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+ ASSERT_NE(nullptr, mirror_layer_cc);
+ EXPECT_EQ(mirror_layer->cc_layer_for_testing(), mirror_layer_cc);
+ EXPECT_EQ(mirrored_layer_1_cc, mirror_layer_cc->mirrored_layer());
+ EXPECT_EQ(1, mirrored_layer_1_cc->mirror_count());
+ EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+
+ // Mirror the second layer. Its mirror count should be increased, but mirror
+ // count for the first mirrored layer should be set back to zero.
+ mirror_layer->MirrorLayer(mirrored_layer_2.get());
+ mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+ ASSERT_NE(nullptr, mirror_layer_cc);
+ EXPECT_EQ(mirror_layer->cc_layer_for_testing(), mirror_layer_cc);
+ EXPECT_EQ(mirrored_layer_2_cc, mirror_layer_cc->mirrored_layer());
+ EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+ EXPECT_EQ(1, mirrored_layer_2_cc->mirror_count());
+
+ // Un-mirror the layer. All mirror counts should be set to zero.
+ mirror_layer->MirrorLayer(nullptr);
+ mirror_layer_cc = mirror_layer->mirror_layer_for_testing();
+ EXPECT_EQ(nullptr, mirror_layer_cc);
+ EXPECT_EQ(0, mirrored_layer_1_cc->mirror_count());
+ EXPECT_EQ(0, mirrored_layer_2_cc->mirror_count());
+}
+
+// Verifies that when a layer is mirroring another layer, its size matches the
+// size of the mirrored layer.
+TEST_F(LayerWithNullDelegateTest, MirrorLayerBounds) {
+ const gfx::Rect mirrored_bounds(0, 0, 50, 50);
+ const gfx::Rect mirror_bounds(0, 50, 10, 10);
+
+ std::unique_ptr<Layer> mirrored_layer(CreateLayer(LAYER_SOLID_COLOR));
+ mirrored_layer->SetBounds(mirrored_bounds);
+
+ std::unique_ptr<Layer> mirror_layer(CreateLayer(LAYER_SOLID_COLOR));
+ mirror_layer->SetBounds(mirror_bounds);
+
+ EXPECT_EQ(mirror_bounds, mirror_layer->bounds());
+
+ mirror_layer->MirrorLayer(mirrored_layer.get());
+ EXPECT_EQ(mirror_bounds.origin(), mirror_layer->bounds().origin());
+ EXPECT_EQ(mirrored_bounds.size(), mirror_layer->bounds().size());
+}
+
void ExpectRgba(int x, int y, SkColor expected_color, SkColor actual_color) {
EXPECT_EQ(expected_color, actual_color)
<< "Pixel error at x=" << x << " y=" << y << "; "