Index: cc/layer_tree_host_unittest_occlusion.cc |
diff --git a/cc/layer_tree_host_unittest_occlusion.cc b/cc/layer_tree_host_unittest_occlusion.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..55141864ceac92a24df9e83256d6ecb19ded92b8 |
--- /dev/null |
+++ b/cc/layer_tree_host_unittest_occlusion.cc |
@@ -0,0 +1,476 @@ |
+// Copyright 2012 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/layer_tree_host.h" |
+ |
+#include "cc/layer.h" |
+#include "cc/test/layer_tree_test_common.h" |
+#include "cc/test/occlusion_tracker_test_common.h" |
+ |
+namespace cc { |
+namespace { |
+ |
+class TestLayer : public Layer { |
+ public: |
+ static scoped_refptr<TestLayer> Create() { |
+ return make_scoped_refptr(new TestLayer()); |
+ } |
+ |
+ virtual void update( |
+ ResourceUpdateQueue& update_queue, |
+ const OcclusionTracker* occlusion, |
+ RenderingStats& stats) OVERRIDE { |
+ if (!occlusion) |
+ return; |
+ |
+ // Gain access to internals of the OcclusionTracker. |
+ const TestOcclusionTracker* test_occlusion = |
+ static_cast<const TestOcclusionTracker*>(occlusion); |
+ occlusion_ = UnionRegions( |
+ test_occlusion->occlusionFromInsideTarget(), |
+ test_occlusion->occlusionFromOutsideTarget()); |
+ } |
+ |
+ const Region& occlusion() const { return occlusion_; } |
+ const Region& expected_occlusion() const { return expected_occlusion_; } |
+ void set_expected_occlusion(const Region& occlusion) { |
+ expected_occlusion_ = occlusion; |
+ } |
+ |
+ private: |
+ TestLayer() : Layer() { |
+ setIsDrawable(true); |
+ } |
+ virtual ~TestLayer() { } |
+ |
+ Region occlusion_; |
+ Region expected_occlusion_; |
+}; |
+ |
+class LayerTreeHostOcclusionTest : public ThreadedTest { |
+ public: |
+ |
+ LayerTreeHostOcclusionTest() |
+ : root_(TestLayer::Create()), |
+ child_(TestLayer::Create()), |
+ child2_(TestLayer::Create()), |
+ grand_child_(TestLayer::Create()), |
+ mask_(TestLayer::Create()) { |
+ } |
+ |
+ virtual void beginTest() OVERRIDE { |
+ postSetNeedsCommitToMainThread(); |
+ } |
+ |
+ virtual void didCommit() OVERRIDE { |
+ TestLayer* root = static_cast<TestLayer*>(m_layerTreeHost->rootLayer()); |
+ VerifyOcclusion(root); |
+ |
+ endTest(); |
+ } |
+ |
+ virtual void afterTest() OVERRIDE {} |
+ |
+ void VerifyOcclusion(TestLayer* layer) const { |
+ EXPECT_EQ(layer->expected_occlusion().ToString(), |
+ layer->occlusion().ToString()); |
+ |
+ for (size_t i = 0; i < layer->children().size(); ++i) { |
+ TestLayer* child = static_cast<TestLayer*>(layer->children()[i].get()); |
+ VerifyOcclusion(child); |
+ } |
+ } |
+ |
+ void SetLayerPropertiesForTesting( |
+ TestLayer* layer, TestLayer* parent, const gfx::Transform& transform, |
+ const gfx::PointF& position, const gfx::Size& bounds, bool opaque) const { |
+ layer->removeAllChildren(); |
+ if (parent) |
+ parent->addChild(layer); |
+ layer->setTransform(transform); |
+ layer->setPosition(position); |
+ layer->setBounds(bounds); |
+ layer->setContentsOpaque(opaque); |
+ |
+ layer->setAnchorPoint(gfx::PointF()); |
+ } |
+ |
+ protected: |
+ scoped_refptr<TestLayer> root_; |
+ scoped_refptr<TestLayer> child_; |
+ scoped_refptr<TestLayer> child2_; |
+ scoped_refptr<TestLayer> grand_child_; |
+ scoped_refptr<TestLayer> mask_; |
+ |
+ gfx::Transform identity_matrix_; |
+}; |
+ |
+ |
+class LayerTreeHostOcclusionTestOcclusionSurfaceClipping : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // The child layer is a surface and the grandChild is opaque, but clipped to |
+ // the child and root |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), false); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); |
+ root_->set_expected_occlusion(gfx::Rect(10, 10, 10, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionSurfaceClipping) |
+ |
+class LayerTreeHostOcclusionTestOcclusionSurfaceClippingOpaque : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // If the child layer is opaque, then it adds to the occlusion seen by the |
+ // root_. |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); |
+ root_->set_expected_occlusion(gfx::Rect(10, 10, 190, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionSurfaceClippingOpaque); |
+ |
+class LayerTreeHostOcclusionTestOcclusionTwoChildren : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // Add a second child to the root layer and the regions should merge |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), false); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ |
+ grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); |
+ root_->set_expected_occlusion(gfx::Rect(10, 10, 20, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionTwoChildren) |
+ |
+class LayerTreeHostOcclusionTestOcclusionMask : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // If the child layer has a mask on it, then it shouldn't contribute to |
+ // occlusion on stuff below it. |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(20.f, 20.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(500, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ child_->setMaskLayer(mask_.get()); |
+ |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 180, 180)); |
+ root_->set_expected_occlusion(gfx::Rect(10, 10, 190, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionMask) |
+ |
+class LayerTreeHostOcclusionTestOcclusionMaskBelowOcclusion : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // If the child layer with a mask is below child2, then child2 should |
+ // contribute to occlusion on everything, and child shouldn't contribute |
+ // to the root_. |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ child_->setMaskLayer(mask_.get()); |
+ |
+ grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); |
+ root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionMaskBelowOcclusion) |
+ |
+class LayerTreeHostOcclusionTestOcclusionOpacity : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // If the child layer has a non-opaque opacity, then it shouldn't |
+ // contribute to occlusion on stuff below it |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ child_->setOpacity(0.5f); |
+ |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); |
+ root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionOpacity) |
+ |
+class LayerTreeHostOcclusionTestOcclusionOpacityBelowOcclusion : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // If the child layer with non-opaque opacity is below child2, then |
+ // child2 should contribute to occlusion on everything, and child shouldn't |
+ // contribute to the root_. |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setForceRenderSurface(true); |
+ child_->setOpacity(0.5f); |
+ |
+ grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); |
+ child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); |
+ root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionOpacityBelowOcclusion) |
+ |
+class LayerTreeHostOcclusionTestOcclusionOpacityFilter : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ gfx::Transform childTransform; |
+ childTransform.Translate(250.0, 250.0); |
+ childTransform.Rotate(90.0); |
+ childTransform.Translate(-250.0, -250.0); |
+ |
+ WebKit::WebFilterOperations filters; |
+ filters.append(WebKit::WebFilterOperation::createOpacityFilter(0.5)); |
+ |
+ // If the child layer has a filter that changes alpha values, and is below |
+ // child2, then child2 should contribute to occlusion on everything, |
+ // and child shouldn't contribute to the root |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), childTransform, |
+ gfx::PointF(30.f, 30.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 70.f), gfx::Size(500, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setFilters(filters); |
+ |
+ grand_child_->set_expected_occlusion(gfx::Rect(40, 330, 130, 190)); |
+ child_->set_expected_occlusion(UnionRegions( |
+ gfx::Rect(10, 330, 160, 170), gfx::Rect(40, 500, 130, 20))); |
+ root_->set_expected_occlusion(gfx::Rect(10, 70, 190, 130)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionOpacityFilter) |
+ |
+class LayerTreeHostOcclusionTestOcclusionBlurFilter : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ gfx::Transform childTransform; |
+ childTransform.Translate(250.0, 250.0); |
+ childTransform.Rotate(90.0); |
+ childTransform.Translate(-250.0, -250.0); |
+ |
+ WebKit::WebFilterOperations filters; |
+ filters.append(WebKit::WebFilterOperation::createBlurFilter(10)); |
+ |
+ // If the child layer has a filter that moves pixels/changes alpha, and is |
+ // below child2, then child should not inherit occlusion from outside its |
+ // subtree, and should not contribute to the root |
+ SetLayerPropertiesForTesting( |
+ root_.get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); |
+ SetLayerPropertiesForTesting( |
+ child_.get(), root_.get(), childTransform, |
+ gfx::PointF(30.f, 30.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ grand_child_.get(), child_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); |
+ SetLayerPropertiesForTesting( |
+ child2_.get(), root_.get(), identity_matrix_, |
+ gfx::PointF(10.f, 70.f), gfx::Size(500, 500), true); |
+ |
+ child_->setMasksToBounds(true); |
+ child_->setFilters(filters); |
+ |
+ child_->set_expected_occlusion(gfx::Rect(10, 330, 160, 170)); |
+ root_->set_expected_occlusion(gfx::Rect(10, 70, 190, 130)); |
+ |
+ m_layerTreeHost->setRootLayer(root_); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F( |
+ LayerTreeHostOcclusionTestOcclusionBlurFilter) |
+ |
+class LayerTreeHostOcclusionTestManySurfaces : |
+ public LayerTreeHostOcclusionTest { |
+ public: |
+ virtual void setupTree() OVERRIDE { |
+ // We create enough RenderSurfaces that it will trigger Vector reallocation |
+ // while computing occlusion. |
+ std::vector<scoped_refptr<TestLayer> > layers; |
+ int num_surfaces = 200; |
+ int root_width = 400; |
+ int root_height = 400; |
+ |
+ for (int i = 0; i < num_surfaces; ++i) { |
+ layers.push_back(TestLayer::Create()); |
+ if (!i) { |
+ SetLayerPropertiesForTesting( |
+ layers.back().get(), NULL, identity_matrix_, |
+ gfx::PointF(0.f, 0.f), |
+ gfx::Size(root_width, root_height), true); |
+ layers.back()->createRenderSurface(); |
+ } else { |
+ SetLayerPropertiesForTesting( |
+ layers.back().get(), layers[layers.size() - 2].get(), |
+ identity_matrix_, |
+ gfx::PointF(1.f, 1.f), |
+ gfx::Size(root_width-i, root_height-i), true); |
+ layers.back()->setForceRenderSurface(true); |
+ } |
+ } |
+ |
+ for (int i = 1; i < num_surfaces; ++i) { |
+ scoped_refptr<TestLayer> child = TestLayer::Create(); |
+ SetLayerPropertiesForTesting( |
+ child.get(), layers[i].get(), identity_matrix_, |
+ gfx::PointF(0.f, 0.f), gfx::Size(root_width, root_height), false); |
+ } |
+ |
+ for (int i = 0; i < num_surfaces-1; ++i) { |
+ gfx::Rect expected_occlusion(1, 1, root_width-i-1, root_height-i-1); |
+ layers[i]->set_expected_occlusion(expected_occlusion); |
+ } |
+ |
+ m_layerTreeHost->setRootLayer(layers[0].get()); |
+ ThreadedTest::setupTree(); |
+ } |
+}; |
+ |
+SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestManySurfaces) |
+ |
+} // namespace |
+} // namespace cc |