[chromium] Don't occlude on main-thread behind layers/surfaces with impl-thread anima...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Mar 2012 01:07:16 +0000 (01:07 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 19 Mar 2012 01:07:16 +0000 (01:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81354

Patch by Dana Jansens <danakj@chromium.org> on 2012-03-18
Reviewed by Adrienne Walker.

Source/WebCore:

Layers and surfaces can have an animating opacity or translation on the
impl thread. In this case, the main thread does not know their actual
values, and treats these values as "unknowns". This means we can't use
them for marking areas of the screen as occluded, and we can't consider
a part of a layer occluded in a space that we can not reliably transform
to.

Unit test: CCOcclusionTrackerTestAnimationOpacity0OnMainThread
           CCOcclusionTrackerTestAnimationOpacity1OnMainThread
           CCOcclusionTrackerTestAnimationTranslateOnMainThread

* platform/graphics/chromium/cc/CCOcclusionTracker.cpp:
(WebCore::layerOpacityKnown):
(WebCore::layerTransformsToTargetKnown):
(WebCore::layerTransformsToScreenKnown):
(WebCore):
(WebCore::surfaceOpacityUnknown):
(WebCore::surfaceTransformsToTargetUnknown):
(WebCore::surfaceTransformsToScreenUnknown):
(WebCore::::finishedTargetRenderSurface):
(WebCore::contentToScreenSpaceTransform):
(WebCore::contentToTargetSurfaceTransform):
(WebCore::::markOccludedBehindLayer):
(WebCore::::occluded):
(WebCore::::unoccludedContentRect):

Source/WebKit/chromium:

* tests/CCOcclusionTrackerTest.cpp:
(WebCore):
(WebCore::addOpacityAnimationToLayer):
(WebCore::addTransformAnimationToLayer):
(CCOcclusionTrackerTestAnimationOpacity1OnMainThread):
(WebCore::CCOcclusionTrackerTestAnimationOpacity1OnMainThread::runMyTest):
(CCOcclusionTrackerTestAnimationOpacity0OnMainThread):
(WebCore::CCOcclusionTrackerTestAnimationOpacity0OnMainThread::runMyTest):
(CCOcclusionTrackerTestAnimationTranslateOnMainThread):
(WebCore::CCOcclusionTrackerTestAnimationTranslateOnMainThread::runMyTest):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111145 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.cpp
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp

index 23995f70827cb75ab325815c14038337c882b52e..68dd8c98a42db6bf793d49815b3031b179f9b982 100644 (file)
@@ -1,3 +1,36 @@
+2012-03-18  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Don't occlude on main-thread behind layers/surfaces with impl-thread animations
+        https://bugs.webkit.org/show_bug.cgi?id=81354
+
+        Reviewed by Adrienne Walker.
+
+        Layers and surfaces can have an animating opacity or translation on the
+        impl thread. In this case, the main thread does not know their actual
+        values, and treats these values as "unknowns". This means we can't use
+        them for marking areas of the screen as occluded, and we can't consider
+        a part of a layer occluded in a space that we can not reliably transform
+        to.
+
+        Unit test: CCOcclusionTrackerTestAnimationOpacity0OnMainThread
+                   CCOcclusionTrackerTestAnimationOpacity1OnMainThread
+                   CCOcclusionTrackerTestAnimationTranslateOnMainThread
+
+        * platform/graphics/chromium/cc/CCOcclusionTracker.cpp:
+        (WebCore::layerOpacityKnown):
+        (WebCore::layerTransformsToTargetKnown):
+        (WebCore::layerTransformsToScreenKnown):
+        (WebCore):
+        (WebCore::surfaceOpacityUnknown):
+        (WebCore::surfaceTransformsToTargetUnknown):
+        (WebCore::surfaceTransformsToScreenUnknown):
+        (WebCore::::finishedTargetRenderSurface):
+        (WebCore::contentToScreenSpaceTransform):
+        (WebCore::contentToTargetSurfaceTransform):
+        (WebCore::::markOccludedBehindLayer):
+        (WebCore::::occluded):
+        (WebCore::::unoccludedContentRect):
+
 2012-03-18  Dana Jansens  <danakj@chromium.org>
 
         [chromium] Animating opacity is not opaque and should create a render surface on main thread
index c69a4d9c81d2148ae0ac7d78990612a8febf95b2..4c617ad67e461f6bf6ef8c62305950b7ea5ccfd4 100644 (file)
@@ -73,6 +73,20 @@ void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::enterTargetRenderSurf
     }
 }
 
+static inline bool layerOpacityKnown(const LayerChromium* layer) { return !layer->drawOpacityIsAnimating(); }
+static inline bool layerOpacityKnown(const CCLayerImpl*) { return true; }
+static inline bool layerTransformsToTargetKnown(const LayerChromium* layer) { return !layer->drawTransformIsAnimating(); }
+static inline bool layerTransformsToTargetKnown(const CCLayerImpl*) { return true; }
+static inline bool layerTransformsToScreenKnown(const LayerChromium* layer) { return !layer->screenSpaceTransformIsAnimating(); }
+static inline bool layerTransformsToScreenKnown(const CCLayerImpl*) { return true; }
+
+static inline bool surfaceOpacityKnown(const RenderSurfaceChromium* surface) { return !surface->drawOpacityIsAnimating(); }
+static inline bool surfaceOpacityKnown(const CCRenderSurface*) { return true; }
+static inline bool surfaceTransformsToTargetKnown(const RenderSurfaceChromium* surface) { return !surface->targetSurfaceTransformsAreAnimating(); }
+static inline bool surfaceTransformsToTargetKnown(const CCRenderSurface*) { return true; }
+static inline bool surfaceTransformsToScreenKnown(const RenderSurfaceChromium* surface) { return !surface->screenSpaceTransformsAreAnimating(); }
+static inline bool surfaceTransformsToScreenKnown(const CCRenderSurface*) { return true; }
+
 template<typename LayerType, typename RenderSurfaceType>
 void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedTargetRenderSurface(const LayerType* owningLayer, const RenderSurfaceType* finishedTarget)
 {
@@ -82,9 +96,15 @@ void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::finishedTargetRenderS
     // Make sure we know about the target surface.
     enterTargetRenderSurface(finishedTarget);
 
-    if (owningLayer->maskLayer() || finishedTarget->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) {
+    // If the occlusion within the surface can not be applied to things outside of the surface's subtree, then clear the occlusion here so it won't be used.
+    if (owningLayer->maskLayer() || !surfaceOpacityKnown(finishedTarget) || finishedTarget->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) {
         m_stack.last().occlusionInScreen = Region();
         m_stack.last().occlusionInTarget = Region();
+    } else {
+        if (!surfaceTransformsToTargetKnown(finishedTarget))
+            m_stack.last().occlusionInTarget = Region();
+        if (!surfaceTransformsToScreenKnown(finishedTarget))
+            m_stack.last().occlusionInScreen = Region();
     }
 }
 
@@ -141,6 +161,7 @@ void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToTargetRenderSu
 template<typename LayerType>
 static inline TransformationMatrix contentToScreenSpaceTransform(const LayerType* layer)
 {
+    ASSERT(layerTransformsToScreenKnown(layer));
     IntSize boundsInLayerSpace = layer->bounds();
     IntSize boundsInContentSpace = layer->contentBounds();
 
@@ -159,6 +180,7 @@ static inline TransformationMatrix contentToScreenSpaceTransform(const LayerType
 template<typename LayerType>
 static inline TransformationMatrix contentToTargetSurfaceTransform(const LayerType* layer)
 {
+    ASSERT(layerTransformsToTargetKnown(layer));
     IntSize boundsInLayerSpace = layer->bounds();
     IntSize boundsInContentSpace = layer->contentBounds();
 
@@ -209,15 +231,14 @@ void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::markOccludedBehindLay
     if (m_stack.isEmpty())
         return;
 
-    if (layer->drawOpacity() != 1)
+    if (!layerOpacityKnown(layer) || layer->drawOpacity() < 1)
         return;
 
-    TransformationMatrix contentToScreenSpace = contentToScreenSpaceTransform<LayerType>(layer);
-    TransformationMatrix contentToTargetSurface = contentToTargetSurfaceTransform<LayerType>(layer);
-
     // FIXME: Remove m_usePaintTracking when paint tracking is on for paint culling.
-    m_stack.last().occlusionInScreen.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToScreenSpace, m_usePaintTracking));
-    m_stack.last().occlusionInTarget.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToTargetSurface, m_usePaintTracking));
+    if (layerTransformsToScreenKnown(layer))
+        m_stack.last().occlusionInScreen.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToScreenSpaceTransform<LayerType>(layer), m_usePaintTracking));
+    if (layerTransformsToTargetKnown(layer))
+        m_stack.last().occlusionInTarget.unite(computeOcclusionBehindLayer<LayerType>(layer, contentToTargetSurfaceTransform<LayerType>(layer), m_usePaintTracking));
 }
 
 static inline bool testContentRectOccluded(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const IntRect& scissorRect, const Region& occlusion)
@@ -239,9 +260,9 @@ bool CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::occluded(const LayerT
 
     ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
 
-    if (testContentRectOccluded(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen))
+    if (layerTransformsToScreenKnown(layer) && testContentRectOccluded(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen))
         return true;
-    if (testContentRectOccluded(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget))
+    if (layerTransformsToTargetKnown(layer) && testContentRectOccluded(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget))
         return true;
     return false;
 }
@@ -307,13 +328,16 @@ IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentR
     // We want to return a rect that contains all the visible parts of |contentRect| in both screen space and in the target surface.
     // So we find the visible parts of |contentRect| in each space, and take the intersection.
 
-    TransformationMatrix contentToScreenSpace = contentToScreenSpaceTransform<LayerType>(layer);
-    TransformationMatrix contentToTargetSurface = contentToTargetSurfaceTransform<LayerType>(layer);
+    IntRect unoccludedInScreen = contentRect;
+    if (layerTransformsToScreenKnown(layer))
+        unoccludedInScreen = computeUnoccludedContentRect(contentRect, contentToScreenSpaceTransform<LayerType>(layer), m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen);
 
-    IntRect unoccludedInScreen = computeUnoccludedContentRect(contentRect, contentToScreenSpace, m_scissorRectInScreenSpace, m_stack.last().occlusionInScreen);
     if (unoccludedInScreen.isEmpty())
-        return IntRect();
-    IntRect unoccludedInTarget = computeUnoccludedContentRect(contentRect, contentToTargetSurface, layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget);
+        return unoccludedInScreen;
+
+    IntRect unoccludedInTarget = contentRect;
+    if (layerTransformsToTargetKnown(layer))
+        unoccludedInTarget = computeUnoccludedContentRect(contentRect, contentToTargetSurfaceTransform<LayerType>(layer), layerScissorRectInTargetSurface(layer), m_stack.last().occlusionInTarget);
 
     return intersection(unoccludedInScreen, unoccludedInTarget);
 }
index 87443b8f52fa510badfa53150425c08f28672566..de2e8b12d4031e234b55b14ff0550a2e864e9bb3 100644 (file)
@@ -1,3 +1,21 @@
+2012-03-18  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Don't occlude on main-thread behind layers/surfaces with impl-thread animations
+        https://bugs.webkit.org/show_bug.cgi?id=81354
+
+        Reviewed by Adrienne Walker.
+
+        * tests/CCOcclusionTrackerTest.cpp:
+        (WebCore):
+        (WebCore::addOpacityAnimationToLayer):
+        (WebCore::addTransformAnimationToLayer):
+        (CCOcclusionTrackerTestAnimationOpacity1OnMainThread):
+        (WebCore::CCOcclusionTrackerTestAnimationOpacity1OnMainThread::runMyTest):
+        (CCOcclusionTrackerTestAnimationOpacity0OnMainThread):
+        (WebCore::CCOcclusionTrackerTestAnimationOpacity0OnMainThread::runMyTest):
+        (CCOcclusionTrackerTestAnimationTranslateOnMainThread):
+        (WebCore::CCOcclusionTrackerTestAnimationTranslateOnMainThread::runMyTest):
+
 2012-03-18  Dana Jansens  <danakj@chromium.org>
 
         [chromium] Animating opacity is not opaque and should create a render surface on main thread
index f220152430a7119b99a10f56a6acea4e58048828..e281415cb3a5b372dbea5b7597841538bc3c364c 100644 (file)
@@ -30,6 +30,8 @@
 #include "LayerChromium.h"
 #include "Region.h"
 #include "TransformationMatrix.h"
+#include "TranslateTransformOperation.h"
+#include "cc/CCLayerAnimationController.h"
 #include "cc/CCLayerImpl.h"
 #include "cc/CCLayerTreeHostCommon.h"
 #include "cc/CCSingleThreadProxy.h"
@@ -1674,4 +1676,206 @@ protected:
 
 MAIN_THREAD_TEST(CCOcclusionTrackerTestPerspectiveTransformBehindCamera);
 
+template<typename LayerType>
+static int addOpacityAnimationToLayer(LayerType* layer, float startValue, float endValue, double duration)
+{
+    static int id = 0;
+    WebCore::KeyframeValueList values(AnimatedPropertyOpacity);
+    values.insert(new FloatAnimationValue(0, startValue));
+    values.insert(new FloatAnimationValue(duration, endValue));
+
+    RefPtr<Animation> animation = Animation::create();
+    animation->setDuration(duration);
+
+    IntSize boxSize;
+    layer->layerAnimationController()->addAnimation(values, boxSize, animation.get(), id, 0, 0);
+    return id++;
+}
+
+template<typename LayerType>
+static int addTransformAnimationToLayer(LayerType* layer, double duration)
+{
+    static int id = 0;
+    WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform);
+
+    TransformOperations operations1;
+    operations1.operations().append(TranslateTransformOperation::create(Length(2, Fixed), Length(0, Fixed), TransformOperation::TRANSLATE_X));
+    values.insert(new TransformAnimationValue(0, &operations1));
+
+    RefPtr<Animation> animation = Animation::create();
+    animation->setDuration(duration);
+
+    IntSize boxSize;
+    layer->layerAnimationController()->addAnimation(values, boxSize, animation.get(), id, 0, 0);
+    return id++;
+}
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestAnimationOpacity1OnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+        typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+        typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+        typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true);
+        typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+
+        addOpacityAnimationToLayer(layer, 0, 1, 10);
+        addOpacityAnimationToLayer(surface, 0, 1, 10);
+        this->calcDrawEtc(parent);
+
+        EXPECT_TRUE(layer->drawOpacityIsAnimating());
+        EXPECT_FALSE(surface->drawOpacityIsAnimating());
+        EXPECT_TRUE(surface->renderSurface()->drawOpacityIsAnimating());
+
+        TestCCOcclusionTrackerBase<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+        occlusion.enterTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surfaceChild2);
+        EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        occlusion.markOccludedBehindLayer(surfaceChild);
+        EXPECT_EQ_RECT(IntRect(200, 0, 100, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        occlusion.markOccludedBehindLayer(surface);
+        EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+        occlusion.leaveToTargetRenderSurface(parent->renderSurface());
+        // Occlusion is lost when leaving the animating surface.
+        EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+
+        occlusion.markOccludedBehindLayer(layer);
+        // Occlusion is not added for the animating layer.
+        EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+    }
+};
+
+MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationOpacity1OnMainThread);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestAnimationOpacity0OnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+        typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+        typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+        typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true);
+        typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+
+        addOpacityAnimationToLayer(layer, 1, 0, 10);
+        addOpacityAnimationToLayer(surface, 1, 0, 10);
+        this->calcDrawEtc(parent);
+
+        EXPECT_TRUE(layer->drawOpacityIsAnimating());
+        EXPECT_FALSE(surface->drawOpacityIsAnimating());
+        EXPECT_TRUE(surface->renderSurface()->drawOpacityIsAnimating());
+
+        TestCCOcclusionTrackerBase<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+        occlusion.enterTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surfaceChild2);
+        EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        occlusion.markOccludedBehindLayer(surfaceChild);
+        EXPECT_EQ_RECT(IntRect(200, 0, 100, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        occlusion.markOccludedBehindLayer(surface);
+        EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+        occlusion.leaveToTargetRenderSurface(parent->renderSurface());
+        // Occlusion is lost when leaving the animating surface.
+        EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+
+        occlusion.markOccludedBehindLayer(layer);
+        // Occlusion is not added for the animating layer.
+        EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+    }
+};
+
+MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationOpacity0OnMainThread);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestAnimationTranslateOnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300));
+        typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+        typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true);
+        typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true);
+        typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true);
+        typename Types::ContentLayerType* surface2 = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(50, 300), true);
+
+        addTransformAnimationToLayer(layer, 10);
+        addTransformAnimationToLayer(surface, 10);
+        addTransformAnimationToLayer(surfaceChild, 10);
+        this->calcDrawEtc(parent);
+
+        EXPECT_TRUE(layer->drawTransformIsAnimating());
+        EXPECT_TRUE(layer->screenSpaceTransformIsAnimating());
+        EXPECT_TRUE(surface->renderSurface()->targetSurfaceTransformsAreAnimating());
+        EXPECT_TRUE(surface->renderSurface()->screenSpaceTransformsAreAnimating());
+        // The surface owning layer doesn't animate against its own surface.
+        EXPECT_FALSE(surface->drawTransformIsAnimating());
+        EXPECT_TRUE(surface->screenSpaceTransformIsAnimating());
+        EXPECT_TRUE(surfaceChild->drawTransformIsAnimating());
+        EXPECT_TRUE(surfaceChild->screenSpaceTransformIsAnimating());
+
+        TestCCOcclusionTrackerBase<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+
+        occlusion.enterTargetRenderSurface(surface2->renderSurface());
+        occlusion.markOccludedBehindLayer(surface2);
+        occlusion.finishedTargetRenderSurface(surface2, surface2->renderSurface());
+        occlusion.leaveToTargetRenderSurface(parent->renderSurface());
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+
+        occlusion.enterTargetRenderSurface(surface->renderSurface());
+
+        // The layer is moving in screen space but not relative to its target, so occlusion should happen in its target space only.
+        // It also means that things occluding in screen space (e.g. surface2) cannot occlude this layer.
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 300), occlusion.unoccludedContentRect(surfaceChild2, IntRect(0, 0, 100, 300)));
+        EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 50, 300)));
+
+        occlusion.markOccludedBehindLayer(surfaceChild2);
+        EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 100, 300)));
+        EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 300), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+        EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+
+        // The surface child is occluded by the surfaceChild2, but is moving relative its target and the screen, so it
+        // can't be occluded.
+        EXPECT_EQ_RECT(IntRect(0, 0, 200, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 200, 300)));
+        EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 50, 300)));
+
+        occlusion.markOccludedBehindLayer(surfaceChild);
+        // The layer is moving in screen space but not relative to its target, so occlusion should happen in its target space only.
+        EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 300), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+        EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+
+        occlusion.markOccludedBehindLayer(surface);
+        // The layer is moving in screen space but not relative to its target, so occlusion should happen in its target space only.
+        EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+        occlusion.leaveToTargetRenderSurface(parent->renderSurface());
+        // The surface is moving in the screen and in its target, so all occlusion is lost when leaving it.
+        EXPECT_EQ_RECT(IntRect(50, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+
+        occlusion.markOccludedBehindLayer(layer);
+        // The layer is animating in the screen and in its target, so no occlusion is added.
+        EXPECT_EQ_RECT(IntRect(50, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+    }
+};
+
+MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationTranslateOnMainThread);
+
 } // namespace