summaryrefslogtreecommitdiff
path: root/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp')
-rw-r--r--Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp759
1 files changed, 751 insertions, 8 deletions
diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp
index e6206cc58..51a032618 100644
--- a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp
+++ b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp
@@ -26,14 +26,19 @@
#include "cc/CCLayerTreeHostCommon.h"
+#include "CCAnimationTestCommon.h"
#include "CCLayerTreeTestCommon.h"
#include "LayerChromium.h"
#include "TransformationMatrix.h"
+#include "TranslateTransformOperation.h"
+#include "cc/CCLayerAnimationController.h"
+#include "cc/CCMathUtil.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace WebCore;
+using namespace WebKitTests;
namespace {
@@ -76,7 +81,7 @@ public:
{
}
- virtual bool drawsContent() const { return true; }
+ virtual bool drawsContent() const OVERRIDE { return true; }
};
TEST(CCLayerTreeHostCommonTest, verifyTransformsForNoOpLayer)
@@ -308,6 +313,56 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleRenderSurface)
EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->originTransform());
EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->drawTransform());
+ // The screen space is the same as the target since the child surface draws into the root.
+ EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->screenSpaceTransform());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyTransformsForReplica)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> childReplica = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->createRenderSurface();
+ parent->addChild(child);
+ child->addChild(grandChild);
+ child->setReplicaLayer(childReplica.get());
+
+ // Child is set up so that a new render surface should be created.
+ child->setOpacity(0.5f);
+
+ TransformationMatrix identityMatrix;
+ TransformationMatrix parentLayerTransform;
+ parentLayerTransform.scale3d(2.0, 2.0, 1.0);
+ TransformationMatrix parentTranslationToAnchor;
+ parentTranslationToAnchor.translate(2.5, 3.0);
+ TransformationMatrix parentSublayerMatrix;
+ parentSublayerMatrix.scale3d(10.0, 10.0, 3.3);
+ TransformationMatrix parentTranslationToCenter;
+ parentTranslationToCenter.translate(5.0, 6.0);
+ TransformationMatrix parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse()
+ * parentTranslationToCenter * parentSublayerMatrix * parentTranslationToCenter.inverse();
+ TransformationMatrix childTranslationToCenter;
+ childTranslationToCenter.translate(8.0, 9.0);
+ TransformationMatrix replicaLayerTransform;
+ replicaLayerTransform.scale3d(3.0, 3.0, 1.0);
+ TransformationMatrix replicaCompositeTransform = parentCompositeTransform * replicaLayerTransform;
+
+ // Child's render surface should not exist yet.
+ ASSERT_FALSE(child->renderSurface());
+
+ setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25f, 0.25f), FloatPoint(2.5f, 3.0f), IntSize(10, 12), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(0.0f, 0.0f), IntSize(16, 18), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(-0.5f, -0.5f), IntSize(1, 1), false);
+ setLayerPropertiesForTesting(childReplica.get(), replicaLayerTransform, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(0.0f, 0.0f), IntSize(0, 0), false);
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ // Render surface should have been created now.
+ ASSERT_TRUE(child->renderSurface());
+ ASSERT_EQ(child->renderSurface(), child->targetRenderSurface());
+
+ EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->targetRenderSurface()->replicaOriginTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->targetRenderSurface()->replicaScreenSpaceTransform());
}
TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
@@ -315,6 +370,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
// This test creates a more complex tree and verifies it all at once. This covers the following cases:
// - layers that are described w.r.t. a render surface: should have draw transforms described w.r.t. that surface
// - A render surface described w.r.t. an ancestor render surface: should have a draw transform described w.r.t. that ancestor surface
+ // - Replicas of a render surface are described w.r.t. the replica's transform around its anchor, along with the surface itself.
// - Sanity check on recursion: verify transforms of layers described w.r.t. a render surface that is described w.r.t. an ancestor render surface.
// - verifying that each layer has a reference to the correct renderSurface and targetRenderSurface values.
@@ -324,6 +380,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
RefPtr<LayerChromium> childOfRoot = LayerChromium::create();
RefPtr<LayerChromium> childOfRS1 = LayerChromium::create();
RefPtr<LayerChromium> childOfRS2 = LayerChromium::create();
+ RefPtr<LayerChromium> replicaOfRS1 = LayerChromium::create();
+ RefPtr<LayerChromium> replicaOfRS2 = LayerChromium::create();
RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create();
RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
@@ -336,6 +394,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
childOfRoot->addChild(grandChildOfRoot);
childOfRS1->addChild(grandChildOfRS1);
childOfRS2->addChild(grandChildOfRS2);
+ renderSurface1->setReplicaLayer(replicaOfRS1.get());
+ renderSurface2->setReplicaLayer(replicaOfRS2.get());
// In combination with descendantDrawsContent, opacity != 1 forces the layer to have a new renderSurface.
renderSurface1->setOpacity(0.5f);
@@ -344,6 +404,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
// All layers in the tree are initialized with an anchor at 2.5 and a size of (10,10).
// matrix "A" is the composite layer transform used in all layers, centered about the anchor point
// matrix "B" is the sublayer transform used in all layers, centered about the center position of the layer.
+ // matrix "R" is the composite replica transform used in all replica layers.
//
// x component tests that layerTransform and sublayerTransform are done in the right order (translation and scale are noncommutative).
// y component has a translation by 1.0 for every ancestor, which indicates the "depth" of the layer in the hierarchy.
@@ -355,9 +416,12 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
layerTransform.translate(1.0, 1.0);
TransformationMatrix sublayerTransform;
sublayerTransform.scale3d(10.0, 1.0, 1.0);
+ TransformationMatrix replicaLayerTransform;
+ replicaLayerTransform.scale3d(-2.0, 5.0, 1.0);
TransformationMatrix A = translationToAnchor * layerTransform * translationToAnchor.inverse();
TransformationMatrix B = translationToCenter * sublayerTransform * translationToCenter.inverse();
+ TransformationMatrix R = A * translationToAnchor * replicaLayerTransform * translationToAnchor.inverse();
setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
@@ -368,6 +432,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(replicaOfRS1.get(), replicaLayerTransform, sublayerTransform, FloatPoint(), FloatPoint(2.5f, 0.0f), IntSize(), false);
+ setLayerPropertiesForTesting(replicaOfRS2.get(), replicaLayerTransform, sublayerTransform, FloatPoint(), FloatPoint(2.5f, 0.0f), IntSize(), false);
executeCalculateDrawTransformsAndVisibility(parent.get());
@@ -433,8 +499,14 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy)
//
// Origin transform of render surface 1 is described with respect to root.
EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->originTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaOriginTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaScreenSpaceTransform());
// Origin transform of render surface 2 is described with respect to render surface 2.
EXPECT_TRANSFORMATION_MATRIX_EQ(B * A, renderSurface2->renderSurface()->originTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(B * R, renderSurface2->renderSurface()->replicaOriginTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, renderSurface2->renderSurface()->screenSpaceTransform());
+ EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * R, renderSurface2->renderSurface()->replicaScreenSpaceTransform());
// Sanity check. If these fail there is probably a bug in the test itself.
// It is expected that we correctly set up transforms so that the y-component of the screen-space transform
@@ -457,7 +529,7 @@ TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceListForClipLayer)
RefPtr<LayerChromium> parent = LayerChromium::create();
RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent());
- renderSurface1->setOpacity(0.9);
+ renderSurface1->setOpacity(0.9f);
const TransformationMatrix identityMatrix;
setLayerPropertiesForTesting(renderSurface1.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false);
@@ -550,9 +622,58 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces)
setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
child->setMasksToBounds(true);
- child->setOpacity(0.4);
- grandChild->setOpacity(0.5);
- greatGrandChild->setOpacity(0.4);
+ child->setOpacity(0.4f);
+ grandChild->setOpacity(0.5f);
+ greatGrandChild->setOpacity(0.4f);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ Vector<RefPtr<LayerChromium> > dummyLayerList;
+ int dummyMaxTextureSize = 512;
+
+ // FIXME: when we fix this "root-layer special case" behavior in CCLayerTreeHost, we will have to fix it here, too.
+ parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds()));
+ renderSurfaceLayerList.append(parent.get());
+
+ CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize);
+
+ ASSERT_EQ(2U, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(child->id(), renderSurfaceLayerList[1]->id());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfacesCrashRepro)
+{
+ // This is a similar situation as verifyClipRectCullsRenderSurfaces, except that
+ // it reproduces a crash bug http://code.google.com/p/chromium/issues/detail?id=106734.
+
+ const TransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild = LayerChromium::create();
+ RefPtr<LayerChromium> greatGrandChild = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->createRenderSurface();
+ parent->addChild(child);
+ child->addChild(grandChild);
+ grandChild->addChild(greatGrandChild);
+
+ // leafNode1 ensures that parent and child are kept on the renderSurfaceLayerList,
+ // even though grandChild and greatGrandChild should be clipped.
+ child->addChild(leafNode1);
+ greatGrandChild->addChild(leafNode2);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(greatGrandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+
+ child->setMasksToBounds(true);
+ child->setOpacity(0.4f);
+ grandChild->setOpacity(0.5f);
+ greatGrandChild->setOpacity(0.4f);
// Contaminate the grandChild and greatGrandChild's clipRect to reproduce the crash
// bug found in http://code.google.com/p/chromium/issues/detail?id=106734. In this
@@ -561,9 +682,6 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces)
// renderSurface remains on the renderSurfaceLayerList, which violates the assumption
// that an empty renderSurface will always be the last item on the list, which
// ultimately caused the crash.
- //
- // FIXME: it is also useful to test with this commented out. Eventually we should
- // create several test cases that test clipRect/drawableContentRect computation.
child->setClipRect(IntRect(IntPoint::zero(), IntSize(20, 20)));
greatGrandChild->setClipRect(IntRect(IntPoint::zero(), IntSize(1234, 1234)));
@@ -582,6 +700,631 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces)
EXPECT_EQ(child->id(), renderSurfaceLayerList[1]->id());
}
+TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToLayers)
+{
+ // Verify that layers get the appropriate clipRects when their parent masksToBounds is true.
+ //
+ // grandChild1 - completely inside the region; clipRect should be the mask region (larger than this layer's bounds).
+ // grandChild2 - partially clipped but NOT masksToBounds; the clipRect should be the parent's clipRect regardless of the layer's bounds.
+ // grandChild3 - partially clipped and masksToBounds; the clipRect will be the intersection of layerBounds and the mask region.
+ // grandChild4 - outside parent's clipRect, and masksToBounds; the clipRect should be empty.
+ //
+
+ const TransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild1 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild3 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild4 = LayerChromium::create();
+
+ parent->createRenderSurface();
+ parent->addChild(child);
+ child->addChild(grandChild1);
+ child->addChild(grandChild2);
+ child->addChild(grandChild3);
+ child->addChild(grandChild4);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(5, 5), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false);
+
+ child->setMasksToBounds(true);
+ grandChild3->setMasksToBounds(true);
+ grandChild4->setMasksToBounds(true);
+
+ // Force everyone to be a render surface.
+ child->setOpacity(0.4f);
+ grandChild1->setOpacity(0.5f);
+ grandChild2->setOpacity(0.5f);
+ grandChild3->setOpacity(0.5f);
+ grandChild4->setOpacity(0.5f);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ Vector<RefPtr<LayerChromium> > dummyLayerList;
+ int dummyMaxTextureSize = 512;
+
+ // FIXME: when we fix this "root-layer special case" behavior in CCLayerTreeHost, we will have to fix it here, too.
+ parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds()));
+ renderSurfaceLayerList.append(parent.get());
+
+ CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize);
+
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(20, 20)), grandChild1->clipRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(20, 20)), grandChild2->clipRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(15, 15), IntSize(5, 5)), grandChild3->clipRect());
+ EXPECT_TRUE(grandChild4->clipRect().isEmpty());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToSurfaces)
+{
+ // Verify that renderSurfaces (and their layers) get the appropriate clipRects when their parent masksToBounds is true.
+ //
+ // Layers that own renderSurfaces (at least for now) do not inherit any clipRect;
+ // instead the surface will enforce the clip for the entire subtree. They may still
+ // have a clipRect of their own layer bounds, however, if masksToBounds was true.
+ //
+
+ const TransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> child = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild1 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild3 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChild4 = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode3 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> leafNode4 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->createRenderSurface();
+ parent->addChild(child);
+ child->addChild(grandChild1);
+ child->addChild(grandChild2);
+ child->addChild(grandChild3);
+ child->addChild(grandChild4);
+
+ // the leaf nodes ensure that these grandChildren become renderSurfaces for this test.
+ grandChild1->addChild(leafNode1);
+ grandChild2->addChild(leafNode2);
+ grandChild3->addChild(leafNode3);
+ grandChild4->addChild(leafNode4);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false);
+ setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false);
+ setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(5, 5), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChild4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(leafNode4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false);
+
+ child->setMasksToBounds(true);
+ grandChild3->setMasksToBounds(true);
+ grandChild4->setMasksToBounds(true);
+
+ // Force everyone to be a render surface.
+ child->setOpacity(0.4f);
+ grandChild1->setOpacity(0.5f);
+ grandChild2->setOpacity(0.5f);
+ grandChild3->setOpacity(0.5f);
+ grandChild4->setOpacity(0.5f);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ Vector<RefPtr<LayerChromium> > dummyLayerList;
+ int dummyMaxTextureSize = 512;
+
+ // FIXME: when we fix this "root-layer special case" behavior in CCLayerTreeHost, we will have to fix it here, too.
+ parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds()));
+ renderSurfaceLayerList.append(parent.get());
+
+ CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize);
+
+ ASSERT_TRUE(grandChild1->renderSurface());
+ ASSERT_TRUE(grandChild2->renderSurface());
+ ASSERT_TRUE(grandChild3->renderSurface());
+ EXPECT_FALSE(grandChild4->renderSurface()); // Because grandChild4 is entirely clipped, it is expected to not have a renderSurface.
+
+ // Surfaces are clipped by their parent, but un-affected by the owning layer's masksToBounds.
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild1->renderSurface()->clipRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild2->renderSurface()->clipRect());
+ EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild3->renderSurface()->clipRect());
+
+ // Layers do not inherit the clipRect from their owned surfaces, but if masksToBounds is true, they do create their own clipRect.
+ EXPECT_FALSE(grandChild1->usesLayerClipping());
+ EXPECT_FALSE(grandChild2->usesLayerClipping());
+ EXPECT_TRUE(grandChild3->usesLayerClipping());
+ EXPECT_TRUE(grandChild4->usesLayerClipping());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyAnimationsForRenderSurfaceHierarchy)
+{
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface1 = LayerChromium::create();
+ RefPtr<LayerChromium> renderSurface2 = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRoot = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRS1 = LayerChromium::create();
+ RefPtr<LayerChromium> childOfRS2 = LayerChromium::create();
+ RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ parent->createRenderSurface();
+ parent->addChild(renderSurface1);
+ parent->addChild(childOfRoot);
+ renderSurface1->addChild(childOfRS1);
+ renderSurface1->addChild(renderSurface2);
+ renderSurface2->addChild(childOfRS2);
+ childOfRoot->addChild(grandChildOfRoot);
+ childOfRS1->addChild(grandChildOfRS1);
+ childOfRS2->addChild(grandChildOfRS2);
+
+ // In combination with descendantDrawsContent, opacity != 1 forces the layer to have a new renderSurface.
+ addOpacityTransitionToController(*renderSurface1->layerAnimationController(), 10, 1, 0, false);
+
+ // Also put an animated opacity on a layer without descendants.
+ addOpacityTransitionToController(*grandChildOfRoot->layerAnimationController(), 10, 1, 0, false);
+
+ TransformationMatrix layerTransform;
+ layerTransform.translate(1.0, 1.0);
+ TransformationMatrix sublayerTransform;
+ sublayerTransform.scale3d(10.0, 1.0, 1.0);
+
+ // In combination with descendantDrawsContent, an animated transform forces the layer to have a new renderSurface.
+ addAnimatedTransformToController(*renderSurface2->layerAnimationController(), 10, 30, 0);
+
+ // Also put transform animations on grandChildOfRoot, and grandChildOfRS2
+ addAnimatedTransformToController(*grandChildOfRoot->layerAnimationController(), 10, 30, 0);
+ addAnimatedTransformToController(*grandChildOfRS2->layerAnimationController(), 10, 30, 0);
+
+ setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(renderSurface2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(childOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+ setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false);
+
+ executeCalculateDrawTransformsAndVisibility(parent.get());
+
+ // Only layers that are associated with render surfaces should have an actual renderSurface() value.
+ //
+ ASSERT_TRUE(parent->renderSurface());
+ ASSERT_FALSE(childOfRoot->renderSurface());
+ ASSERT_FALSE(grandChildOfRoot->renderSurface());
+
+ ASSERT_TRUE(renderSurface1->renderSurface());
+ ASSERT_FALSE(childOfRS1->renderSurface());
+ ASSERT_FALSE(grandChildOfRS1->renderSurface());
+
+ ASSERT_TRUE(renderSurface2->renderSurface());
+ ASSERT_FALSE(childOfRS2->renderSurface());
+ ASSERT_FALSE(grandChildOfRS2->renderSurface());
+
+ // Verify all targetRenderSurface accessors
+ //
+ EXPECT_EQ(parent->renderSurface(), parent->targetRenderSurface());
+ EXPECT_EQ(parent->renderSurface(), childOfRoot->targetRenderSurface());
+ EXPECT_EQ(parent->renderSurface(), grandChildOfRoot->targetRenderSurface());
+
+ EXPECT_EQ(renderSurface1->renderSurface(), renderSurface1->targetRenderSurface());
+ EXPECT_EQ(renderSurface1->renderSurface(), childOfRS1->targetRenderSurface());
+ EXPECT_EQ(renderSurface1->renderSurface(), grandChildOfRS1->targetRenderSurface());
+
+ EXPECT_EQ(renderSurface2->renderSurface(), renderSurface2->targetRenderSurface());
+ EXPECT_EQ(renderSurface2->renderSurface(), childOfRS2->targetRenderSurface());
+ EXPECT_EQ(renderSurface2->renderSurface(), grandChildOfRS2->targetRenderSurface());
+
+ // Verify drawOpacityIsAnimating values
+ //
+ EXPECT_FALSE(parent->drawOpacityIsAnimating());
+ EXPECT_FALSE(childOfRoot->drawOpacityIsAnimating());
+ EXPECT_TRUE(grandChildOfRoot->drawOpacityIsAnimating());
+ EXPECT_FALSE(renderSurface1->drawOpacityIsAnimating());
+ EXPECT_TRUE(renderSurface1->renderSurface()->drawOpacityIsAnimating());
+ EXPECT_FALSE(childOfRS1->drawOpacityIsAnimating());
+ EXPECT_FALSE(grandChildOfRS1->drawOpacityIsAnimating());
+ EXPECT_FALSE(renderSurface2->drawOpacityIsAnimating());
+ EXPECT_FALSE(renderSurface2->renderSurface()->drawOpacityIsAnimating());
+ EXPECT_FALSE(childOfRS2->drawOpacityIsAnimating());
+ EXPECT_FALSE(grandChildOfRS2->drawOpacityIsAnimating());
+
+ // Verify drawTransformsAnimatingInTarget values
+ //
+ EXPECT_FALSE(parent->drawTransformIsAnimating());
+ EXPECT_FALSE(childOfRoot->drawTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRoot->drawTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->drawTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->renderSurface()->targetSurfaceTransformsAreAnimating());
+ EXPECT_FALSE(childOfRS1->drawTransformIsAnimating());
+ EXPECT_FALSE(grandChildOfRS1->drawTransformIsAnimating());
+ EXPECT_FALSE(renderSurface2->drawTransformIsAnimating());
+ EXPECT_TRUE(renderSurface2->renderSurface()->targetSurfaceTransformsAreAnimating());
+ EXPECT_FALSE(childOfRS2->drawTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRS2->drawTransformIsAnimating());
+
+ // Verify drawTransformsAnimatingInScreen values
+ //
+ EXPECT_FALSE(parent->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(childOfRoot->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRoot->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(renderSurface1->renderSurface()->screenSpaceTransformsAreAnimating());
+ EXPECT_FALSE(childOfRS1->screenSpaceTransformIsAnimating());
+ EXPECT_FALSE(grandChildOfRS1->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(renderSurface2->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(renderSurface2->renderSurface()->screenSpaceTransformsAreAnimating());
+ EXPECT_TRUE(childOfRS2->screenSpaceTransformIsAnimating());
+ EXPECT_TRUE(grandChildOfRS2->screenSpaceTransformIsAnimating());
+
+
+ // Sanity check. If these fail there is probably a bug in the test itself.
+ // It is expected that we correctly set up transforms so that the y-component of the screen-space transform
+ // encodes the "depth" of the layer in the tree.
+ EXPECT_FLOAT_EQ(1.0, parent->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(2.0, childOfRoot->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(3.0, grandChildOfRoot->screenSpaceTransform().m42());
+
+ EXPECT_FLOAT_EQ(2.0, renderSurface1->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(3.0, childOfRS1->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(4.0, grandChildOfRS1->screenSpaceTransform().m42());
+
+ EXPECT_FLOAT_EQ(3.0, renderSurface2->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(4.0, childOfRS2->screenSpaceTransform().m42());
+ EXPECT_FLOAT_EQ(5.0, grandChildOfRS2->screenSpaceTransform().m42());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForIdentityTransform)
+{
+ // Test the calculateVisibleRect() function works correctly for identity transforms.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Layer is contained within the surface.
+ IntRect layerContentRect = IntRect(IntPoint(10, 10), IntSize(30, 30));
+ IntRect expected = IntRect(IntPoint(10, 10), IntSize(30, 30));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Layer is outside the surface rect.
+ layerContentRect = IntRect(IntPoint(120, 120), IntSize(30, 30));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_TRUE(actual.isEmpty());
+
+ // Case 3: Layer is partially overlapping the surface rect.
+ layerContentRect = IntRect(IntPoint(80, 80), IntSize(30, 30));
+ expected = IntRect(IntPoint(80, 80), IntSize(20, 20));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForTranslations)
+{
+ // Test the calculateVisibleRect() function works correctly for scaling transforms.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Layer is contained within the surface.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(10, 10);
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Layer is outside the surface rect.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(120, 120);
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_TRUE(actual.isEmpty());
+
+ // Case 3: Layer is partially overlapping the surface rect.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(80, 80);
+ expected = IntRect(IntPoint(0, 0), IntSize(20, 20));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor2DRotations)
+{
+ // Test the calculateVisibleRect() function works correctly for rotations about z-axis (i.e. 2D rotations).
+ // Remember that calculateVisibleRect() should return the visible rect in the layer's space.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Layer is contained within the surface.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(50, 50);
+ layerToSurfaceTransform.rotate(45);
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Layer is outside the surface rect.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(-50, 0);
+ layerToSurfaceTransform.rotate(45);
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_TRUE(actual.isEmpty());
+
+ // Case 3: The layer is rotated about its top-left corner. In surface space, the layer
+ // is oriented diagonally, with the left half outside of the renderSurface. In
+ // this case, the visible rect should still be the entire layer (remember the
+ // visible rect is computed in layer space); both the top-left and
+ // bottom-right corners of the layer are still visible.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.rotate(45);
+ expected = IntRect(IntPoint(0, 0), IntSize(30, 30));
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 4: The layer is rotated about its top-left corner, and translated upwards. In
+ // surface space, the layer is oriented diagonally, with only the top corner
+ // of the surface overlapping the layer. In layer space, the render surface
+ // overlaps the right side of the layer. The visible rect should be the
+ // layer's right half.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(0, -sqrt(2.0) * 15);
+ layerToSurfaceTransform.rotate(45);
+ expected = IntRect(IntPoint(15, 0), IntSize(15, 30)); // right half of layer bounds.
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicTransform)
+{
+ // Test that the calculateVisibleRect() function works correctly for 3d transforms.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Orthographic projection of a layer rotated about y-axis by 45 degrees, should be fully contained in the renderSurface.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: Orthographic projection of a layer rotated about y-axis by 45 degrees, but
+ // shifted to the side so only the right-half the layer would be visible on
+ // the surface.
+ double halfWidthOfRotatedLayer = (100.0 / sqrt(2.0)) * 0.5; // 100.0 is the un-rotated layer width; divided by sqrt(2.0) is the rotated width.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(-halfWidthOfRotatedLayer, 0);
+ layerToSurfaceTransform.rotate3d(0, 45, 0); // rotates about the left edge of the layer
+ expected = IntRect(IntPoint(50, 0), IntSize(50, 100)); // right half of the layer.
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveTransform)
+{
+ // Test the calculateVisibleRect() function works correctly when the layer has a
+ // perspective projection onto the target surface.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(-50, -50), IntSize(200, 200));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // Case 1: Even though the layer is twice as large as the surface, due to perspective
+ // foreshortening, the layer will fit fully in the surface when its translated
+ // more than the perspective amount.
+ layerToSurfaceTransform.makeIdentity();
+
+ // The following sequence of transforms applies the perspective about the center of the surface.
+ layerToSurfaceTransform.translate(50, 50);
+ layerToSurfaceTransform.applyPerspective(9);
+ layerToSurfaceTransform.translate(-50, -50);
+
+ // This translate places the layer in front of the surface's projection plane.
+ layerToSurfaceTransform.translate3d(0, 0, -27);
+
+ IntRect expected = IntRect(IntPoint(-50, -50), IntSize(200, 200));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+
+ // Case 2: same projection as before, except that the layer is also translated to the
+ // side, so that only the right half of the layer should be visible.
+ //
+ // Explanation of expected result:
+ // The perspective ratio is (z distance between layer and camera origin) / (z distance between projection plane and camera origin) == ((-27 - 9) / 9)
+ // Then, by similar triangles, if we want to move a layer by translating -50 units in projected surface units (so that only half of it is
+ // visible), then we would need to translate by (-36 / 9) * -50 == -200 in the layer's units.
+ //
+ layerToSurfaceTransform.translate3d(-200, 0, 0);
+ expected = IntRect(IntPoint(50, -50), IntSize(100, 200)); // The right half of the layer's bounding rect.
+ actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicIsNotClippedBehindSurface)
+{
+ // There is currently no explicit concept of an orthographic projection plane in our
+ // code (nor in the CSS spec to my knowledge). Therefore, layers that are technically
+ // behind the surface in an orthographic world should not be clipped when they are
+ // flattened to the surface.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // This sequence of transforms effectively rotates the layer about the y-axis at the
+ // center of the layer.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.translate(50, 0);
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+ layerToSurfaceTransform.translate(-50, 0);
+
+ IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveIsClipped)
+{
+ // Test the calculateVisibleRect() function works correctly when projecting a surface
+ // onto a layer, but the layer is partially behind the camera (not just behind the
+ // projection plane). In this case, the cartesian coordinates may seem to be valid,
+ // but actually they are not. The visibleRect needs to be properly clipped by the
+ // w = 0 plane in homogeneous coordinates before converting to cartesian coordinates.
+
+ IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(-10, -1), IntSize(20, 2));
+ TransformationMatrix layerToSurfaceTransform;
+
+ // The layer is positioned so that the right half of the layer should be in front of
+ // the camera, while the other half is behind the surface's projection plane. The
+ // following sequence of transforms applies the perspective and rotation about the
+ // center of the layer.
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.applyPerspective(1);
+ layerToSurfaceTransform.translate3d(0, 0, 1);
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+
+ // Sanity check that this transform does indeed cause w < 0 when applying the
+ // transform, otherwise this code is not testing the intended scenario.
+ bool clipped = false;
+ CCMathUtil::mapQuad(layerToSurfaceTransform, FloatQuad(FloatRect(layerContentRect)), clipped);
+ ASSERT_TRUE(clipped);
+
+ int expectedXPosition = -10;
+ int expectedWidth = 10;
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_EQ(expectedXPosition, actual.x());
+ EXPECT_EQ(expectedWidth, actual.width());
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForPerspectiveUnprojection)
+{
+ // To determine visibleRect in layer space, there needs to be an un-projection from
+ // surface space to layer space. When the original transform was a perspective
+ // projection that was clipped, it returns a rect that encloses the clipped bounds.
+ // Un-projecting this new rect may require clipping again.
+
+ // This sequence of transforms causes one corner of the layer to protrude across the w = 0 plane, and should be clipped.
+ IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100));
+ IntRect layerContentRect = IntRect(IntPoint(-10, -10), IntSize(20, 20));
+ TransformationMatrix layerToSurfaceTransform;
+ layerToSurfaceTransform.makeIdentity();
+ layerToSurfaceTransform.applyPerspective(1);
+ layerToSurfaceTransform.translate3d(0, 0, -5);
+ layerToSurfaceTransform.rotate3d(0, 45, 0);
+ layerToSurfaceTransform.rotate3d(80, 0, 0);
+
+ // Sanity check that un-projection does indeed cause w < 0, otherwise this code is not
+ // testing the intended scenario.
+ bool clipped = false;
+ FloatRect clippedRect = CCMathUtil::mapClippedRect(layerToSurfaceTransform, layerContentRect);
+ CCMathUtil::projectQuad(layerToSurfaceTransform.inverse(), FloatQuad(clippedRect), clipped);
+ ASSERT_TRUE(clipped);
+
+ // Only the corner of the layer is not visible on the surface because of being
+ // clipped. But, the net result of rounding visible region to an axis-aligned rect is
+ // that the entire layer should still be considered visible.
+ IntRect expected = IntRect(IntPoint(-10, -10), IntSize(20, 20));
+ IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform);
+ EXPECT_INT_RECT_EQ(expected, actual);
+}
+
+TEST(CCLayerTreeHostCommonTest, verifyBackFaceCulling)
+{
+ // Verify that layers are appropriately culled when their back face is showing and they are not double sided.
+ //
+ // Layers that are animating do not get culled on the main thread, as their transforms should be
+ // treated as "unknown" so we can not be sure that their back face is really showing.
+ //
+
+ const TransformationMatrix identityMatrix;
+ RefPtr<LayerChromium> parent = LayerChromium::create();
+ RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> animatingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> childOfAnimatingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> animatingChild = adoptRef(new LayerChromiumWithForcedDrawsContent());
+ RefPtr<LayerChromiumWithForcedDrawsContent> child2 = adoptRef(new LayerChromiumWithForcedDrawsContent());
+
+ parent->createRenderSurface();
+ parent->addChild(child);
+ parent->addChild(animatingSurface);
+ animatingSurface->addChild(childOfAnimatingSurface);
+ parent->addChild(animatingChild);
+ parent->addChild(child2);
+
+ // Nothing is double-sided
+ child->setDoubleSided(false);
+ child2->setDoubleSided(false);
+ animatingSurface->setDoubleSided(false);
+ childOfAnimatingSurface->setDoubleSided(false);
+ animatingChild->setDoubleSided(false);
+
+ TransformationMatrix backfaceMatrix;
+ backfaceMatrix.translate(50, 50);
+ backfaceMatrix.rotate3d(0, 1, 0, 180);
+ backfaceMatrix.translate(-50, -50);
+
+ // Having a descendent, and animating transforms, will make the animatingSurface own a render surface.
+ addAnimatedTransformToController(*animatingSurface->layerAnimationController(), 10, 30, 0);
+ // This is just an animating layer, not a surface.
+ addAnimatedTransformToController(*animatingChild->layerAnimationController(), 10, 30, 0);
+
+ setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true);
+ setLayerPropertiesForTesting(child.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(animatingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(childOfAnimatingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(animatingChild.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+ setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false);
+
+ Vector<RefPtr<LayerChromium> > renderSurfaceLayerList;
+ Vector<RefPtr<LayerChromium> > dummyLayerList;
+ int dummyMaxTextureSize = 512;
+
+ parent->renderSurface()->setContentRect(IntRect(IntPoint(), parent->bounds()));
+ parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds()));
+ renderSurfaceLayerList.append(parent.get());
+
+ CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize);
+
+ EXPECT_FALSE(child->renderSurface());
+ EXPECT_TRUE(animatingSurface->renderSurface());
+ EXPECT_FALSE(childOfAnimatingSurface->renderSurface());
+ EXPECT_FALSE(animatingChild->renderSurface());
+ EXPECT_FALSE(child2->renderSurface());
+
+ // Verify that the animatingChild and childOfAnimatingSurface were not culled, but that child was.
+ ASSERT_EQ(2u, renderSurfaceLayerList.size());
+ EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id());
+ EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[1]->id());
+
+ // The non-animating child be culled from the layer list for the parent render surface.
+ ASSERT_EQ(3u, renderSurfaceLayerList[0]->renderSurface()->layerList().size());
+ EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(animatingChild->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[1]->id());
+ EXPECT_EQ(child2->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[2]->id());
+
+ ASSERT_EQ(2u, renderSurfaceLayerList[1]->renderSurface()->layerList().size());
+ EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id());
+ EXPECT_EQ(childOfAnimatingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id());
+
+ EXPECT_FALSE(child2->visibleLayerRect().isEmpty());
+
+ // But if the back face is visible, then the visibleLayerRect should be empty.
+ EXPECT_TRUE(animatingChild->visibleLayerRect().isEmpty());
+ EXPECT_TRUE(animatingSurface->visibleLayerRect().isEmpty());
+ // And any layers in the subtree should not be considered visible either.
+ EXPECT_TRUE(childOfAnimatingSurface->visibleLayerRect().isEmpty());
+}
+
// FIXME:
// continue working on https://bugs.webkit.org/show_bug.cgi?id=68942
// - add a test to verify clipping that changes the "center point"