Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp (113601 => 113602)
--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp 2012-04-09 18:56:26 UTC (rev 113601)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.cpp 2012-04-09 18:58:08 UTC (rev 113602)
@@ -42,6 +42,20 @@
namespace WebCore {
+template<typename LayerType>
+static inline bool backFaceIsVisible(LayerType* layer)
+{
+ // This is checked by computing the transformed normal of the layer in screen space.
+ FloatRect layerRect(FloatPoint(0, 0), FloatSize(layer->bounds()));
+ FloatQuad mappedLayer = layer->screenSpaceTransform().mapQuad(FloatQuad(layerRect));
+ FloatSize horizontalDir = mappedLayer.p2() - mappedLayer.p1();
+ FloatSize verticalDir = mappedLayer.p4() - mappedLayer.p1();
+ FloatPoint3D xAxis(horizontalDir.width(), horizontalDir.height(), 0);
+ FloatPoint3D yAxis(verticalDir.width(), verticalDir.height(), 0);
+ FloatPoint3D zAxis = xAxis.cross(yAxis);
+ return zAxis.z() < 0;
+}
+
IntRect CCLayerTreeHostCommon::calculateVisibleRect(const IntRect& targetSurfaceRect, const IntRect& layerBoundRect, const TransformationMatrix& transform)
{
// Is this layer fully contained within the target surface?
@@ -65,6 +79,39 @@
return layerRect;
}
+template<typename LayerType>
+static IntRect calculateVisibleLayerRect(LayerType* layer)
+{
+ ASSERT(layer->targetRenderSurface());
+
+ // Animated layers can exist in the render surface tree that are not visible currently
+ // and have their back face showing. In this case, their visible rect should be empty.
+ if (!layer->doubleSided() && backFaceIsVisible(layer))
+ return IntRect();
+
+ IntRect targetSurfaceRect = layer->targetRenderSurface()->contentRect();
+
+ if (layer->usesLayerClipping())
+ targetSurfaceRect.intersect(layer->clipRect());
+
+ if (targetSurfaceRect.isEmpty() || layer->contentBounds().isEmpty())
+ return targetSurfaceRect;
+
+ // Note carefully these are aliases
+ const IntSize& bounds = layer->bounds();
+ const IntSize& contentBounds = layer->contentBounds();
+
+ const IntRect layerBoundRect = IntRect(IntPoint(), contentBounds);
+ TransformationMatrix transform = layer->drawTransform();
+
+ transform.scaleNonUniform(bounds.width() / static_cast<double>(contentBounds.width()),
+ bounds.height() / static_cast<double>(contentBounds.height()));
+ transform.translate(-contentBounds.width() / 2.0, -contentBounds.height() / 2.0);
+
+ IntRect visibleLayerRect = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerBoundRect, transform);
+ return visibleLayerRect;
+}
+
static bool isScaleOrTranslation(const TransformationMatrix& m)
{
return !m.m12() && !m.m13() && !m.m14()
@@ -73,6 +120,39 @@
&& m.m44();
}
+static inline bool layerOpacityIsOpaque(CCLayerImpl* layer)
+{
+ return layer->opacity() == 1;
+}
+
+static inline bool layerOpacityIsOpaque(LayerChromium* layer)
+{
+ // If the opacity is being animated then the opacity on the main thread is unreliable
+ // (since the impl thread may be using a different opacity), so it should not be trusted.
+ // In particular, it should not be treated as opaque.
+ return layer->opacity() == 1 && !layer->opacityIsAnimating();
+}
+
+static inline bool transformToParentIsKnown(CCLayerImpl*)
+{
+ return true;
+}
+
+static inline bool transformToParentIsKnown(LayerChromium* layer)
+{
+ return !layer->transformIsAnimating();
+}
+
+static inline bool transformToScreenIsKnown(CCLayerImpl*)
+{
+ return true;
+}
+
+static inline bool transformToScreenIsKnown(LayerChromium* layer)
+{
+ return !layer->screenSpaceTransformIsAnimating();
+}
+
template<typename LayerType>
static bool layerShouldBeSkipped(LayerType* layer)
{
@@ -93,19 +173,9 @@
if (!layer->drawsContent() || layer->bounds().isEmpty())
return true;
- // The layer should not be drawn if (1) it is not double-sided and (2) the back of the layer is facing the screen.
- // This second condition is checked by computing the transformed normal of the layer.
- if (!layer->doubleSided()) {
- FloatRect layerRect(FloatPoint(0, 0), FloatSize(layer->bounds()));
- FloatQuad mappedLayer = layer->screenSpaceTransform().mapQuad(FloatQuad(layerRect));
- FloatSize horizontalDir = mappedLayer.p2() - mappedLayer.p1();
- FloatSize verticalDir = mappedLayer.p4() - mappedLayer.p1();
- FloatPoint3D xAxis(horizontalDir.width(), horizontalDir.height(), 0);
- FloatPoint3D yAxis(verticalDir.width(), verticalDir.height(), 0);
- FloatPoint3D zAxis = xAxis.cross(yAxis);
- if (zAxis.z() < 0)
- return true;
- }
+ // The layer should not be drawn if (1) it is not double-sided and (2) the back of the layer is known to be facing the screen.
+ if (!layer->doubleSided() && transformToScreenIsKnown(layer) && backFaceIsVisible(layer))
+ return true;
return false;
}
@@ -126,29 +196,6 @@
return !layer->opacity() && !layer->opacityIsAnimating();
}
-static inline bool layerOpacityIsOpaque(CCLayerImpl* layer)
-{
- return layer->opacity() == 1;
-}
-
-static inline bool layerOpacityIsOpaque(LayerChromium* layer)
-{
- // If the opacity is being animated then the opacity on the main thread is unreliable
- // (since the impl thread may be using a different opacity), so it should not be trusted.
- // In particular, it should not be treated as opaque.
- return layer->opacity() == 1 && !layer->opacityIsAnimating();
-}
-
-static inline bool transformToParentIsKnown(CCLayerImpl*)
-{
- return true;
-}
-
-static inline bool transformToParentIsKnown(LayerChromium* layer)
-{
- return !layer->transformIsAnimating();
-}
-
template<typename LayerType>
static bool subtreeShouldRenderToSeparateSurface(LayerType* layer, bool axisAlignedWithRespectToParent)
{
@@ -615,7 +662,7 @@
CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
for (CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
if (!it.representsTargetRenderSurface()) {
- IntRect visibleLayerRect = CCLayerTreeHostCommon::calculateVisibleLayerRect<LayerType>(*it);
+ IntRect visibleLayerRect = calculateVisibleLayerRect(*it);
it->setVisibleLayerRect(visibleLayerRect);
}
}
Modified: trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.h (113601 => 113602)
--- trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.h 2012-04-09 18:56:26 UTC (rev 113601)
+++ trunk/Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostCommon.h 2012-04-09 18:58:08 UTC (rev 113602)
@@ -40,7 +40,6 @@
class CCLayerTreeHostCommon {
public:
static IntRect calculateVisibleRect(const IntRect& targetSurfaceRect, const IntRect& layerBoundRect, const TransformationMatrix&);
- template<typename LayerType> static IntRect calculateVisibleLayerRect(LayerType*);
static void calculateDrawTransformsAndVisibility(LayerChromium*, LayerChromium* rootLayer, const TransformationMatrix& parentMatrix, const TransformationMatrix& fullHierarchyMatrix, Vector<RefPtr<LayerChromium> >& renderSurfaceLayerList, Vector<RefPtr<LayerChromium> >& layerList, int maxTextureSize);
static void calculateDrawTransformsAndVisibility(CCLayerImpl*, CCLayerImpl* rootLayer, const TransformationMatrix& parentMatrix, const TransformationMatrix& fullHierarchyMatrix, Vector<CCLayerImpl*>& renderSurfaceLayerList, Vector<CCLayerImpl*>& layerList, CCLayerSorter*, int maxTextureSize);
@@ -59,33 +58,6 @@
};
template<typename LayerType>
-IntRect CCLayerTreeHostCommon::calculateVisibleLayerRect(LayerType* layer)
-{
- ASSERT(layer->targetRenderSurface());
- IntRect targetSurfaceRect = layer->targetRenderSurface()->contentRect();
-
- if (layer->usesLayerClipping())
- targetSurfaceRect.intersect(layer->clipRect());
-
- if (targetSurfaceRect.isEmpty() || layer->contentBounds().isEmpty())
- return targetSurfaceRect;
-
- // Note carefully these are aliases
- const IntSize& bounds = layer->bounds();
- const IntSize& contentBounds = layer->contentBounds();
-
- const IntRect layerBoundRect = IntRect(IntPoint(), contentBounds);
- TransformationMatrix transform = layer->drawTransform();
-
- transform.scaleNonUniform(bounds.width() / static_cast<double>(contentBounds.width()),
- bounds.height() / static_cast<double>(contentBounds.height()));
- transform.translate(-contentBounds.width() / 2.0, -contentBounds.height() / 2.0);
-
- IntRect visibleLayerRect = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerBoundRect, transform);
- return visibleLayerRect;
-}
-
-template<typename LayerType>
bool CCLayerTreeHostCommon::renderSurfaceContributesToTarget(LayerType* layer, int targetSurfaceLayerID)
{
// A layer will either contribute its own content, or its render surface's content, to
Modified: trunk/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp (113601 => 113602)
--- trunk/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp 2012-04-09 18:56:26 UTC (rev 113601)
+++ trunk/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp 2012-04-09 18:58:08 UTC (rev 113602)
@@ -1238,6 +1238,93 @@
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"