[chromium] Cull occluded surface quads
authordanakj@chromium.org <danakj@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Apr 2012 01:23:21 +0000 (01:23 +0000)
committerdanakj@chromium.org <danakj@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 10 Apr 2012 01:23:21 +0000 (01:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81227

Reviewed by Adrienne Walker.

Source/WebCore:

Cull render surface and replica quads during draw based on occlusion.

In order to compute occlusion for the surface or replica, it must not
consider occlusion found in the surface's own subtree. To do this, we
reorder the calls to the occlusion tracker, and add the surface and
replica quads before we "leaveToTargetRenderSurface" which merges
occlusion from the surface's subtree up into its parent.

Unit test: CCOcclusionTrackerTestReplicaOccluded
           CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded
           CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently
           CCOcclusionTrackerTestSurfaceChildOfSurface
           CCOcclusionTrackerTestSurfaceChildOfClippingSurface

And added some sanity checks to other CCOcclusionTracker tests that have
render surfaces.

* platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
(WebCore::CCLayerTreeHostImpl::calculateRenderPasses):
* platform/graphics/chromium/cc/CCOcclusionTracker.cpp:
(WebCore::::leaveToTargetRenderSurface):
(WebCore::::unoccludedContributingSurfaceContentRect):
(WebCore):
* platform/graphics/chromium/cc/CCOcclusionTracker.h:
(CCOcclusionTrackerBase):
* platform/graphics/chromium/cc/CCQuadCuller.cpp:
(WebCore::CCQuadCuller::CCQuadCuller):
(WebCore::appendQuadInternal):
(WebCore::CCQuadCuller::append):
(WebCore):
(WebCore::CCQuadCuller::appendSurface):
(WebCore::CCQuadCuller::appendReplica):
* platform/graphics/chromium/cc/CCQuadCuller.h:
(CCQuadCuller):
* platform/graphics/chromium/cc/CCRenderPass.cpp:
(WebCore::CCRenderPass::appendQuadsForRenderSurfaceLayer):
* platform/graphics/chromium/cc/CCRenderPass.h:
(CCRenderPass):

Source/WebKit/chromium:

* tests/CCOcclusionTrackerTest.cpp:
(WebKitTests::CCOcclusionTrackerTest::TearDown):
(WebKitTests::CCOcclusionTrackerTest::createReplicaLayer):
(CCOcclusionTrackerTest):
(WebKitTests::CCOcclusionTrackerTest::setReplica):
(WebKitTests::CCOcclusionTrackerTestOverlappingSurfaceSiblings::runMyTest):
(WebKitTests::CCOcclusionTrackerTestOverlappingSurfaceSiblingsWithTwoTransforms::runMyTest):
(WebKitTests::CCOcclusionTrackerTestAnimationOpacity1OnMainThread::runMyTest):
(WebKitTests::CCOcclusionTrackerTestAnimationOpacity0OnMainThread::runMyTest):
(WebKitTests::CCOcclusionTrackerTestAnimationTranslateOnMainThread::runMyTest):
(WebKitTests::CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping::runMyTest):
(WebKitTests):
(CCOcclusionTrackerTestReplicaOccluded):
(WebKitTests::CCOcclusionTrackerTestReplicaOccluded::runMyTest):
(CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded):
(WebKitTests::CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded::runMyTest):
(CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently):
(WebKitTests::CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently::runMyTest):
(CCOcclusionTrackerTestSurfaceChildOfSurface):
(WebKitTests::CCOcclusionTrackerTestSurfaceChildOfSurface::runMyTest):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp
Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.cpp
Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.h
Source/WebCore/platform/graphics/chromium/cc/CCQuadCuller.cpp
Source/WebCore/platform/graphics/chromium/cc/CCQuadCuller.h
Source/WebCore/platform/graphics/chromium/cc/CCRenderPass.cpp
Source/WebCore/platform/graphics/chromium/cc/CCRenderPass.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp

index d47ef60..85c6a02 100644 (file)
@@ -1,3 +1,49 @@
+2012-04-09  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Cull occluded surface quads
+        https://bugs.webkit.org/show_bug.cgi?id=81227
+
+        Reviewed by Adrienne Walker.
+
+        Cull render surface and replica quads during draw based on occlusion.
+
+        In order to compute occlusion for the surface or replica, it must not
+        consider occlusion found in the surface's own subtree. To do this, we
+        reorder the calls to the occlusion tracker, and add the surface and
+        replica quads before we "leaveToTargetRenderSurface" which merges
+        occlusion from the surface's subtree up into its parent.
+
+        Unit test: CCOcclusionTrackerTestReplicaOccluded
+                   CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded
+                   CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently
+                   CCOcclusionTrackerTestSurfaceChildOfSurface
+                   CCOcclusionTrackerTestSurfaceChildOfClippingSurface
+
+        And added some sanity checks to other CCOcclusionTracker tests that have
+        render surfaces.
+
+        * platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp:
+        (WebCore::CCLayerTreeHostImpl::calculateRenderPasses):
+        * platform/graphics/chromium/cc/CCOcclusionTracker.cpp:
+        (WebCore::::leaveToTargetRenderSurface):
+        (WebCore::::unoccludedContributingSurfaceContentRect):
+        (WebCore):
+        * platform/graphics/chromium/cc/CCOcclusionTracker.h:
+        (CCOcclusionTrackerBase):
+        * platform/graphics/chromium/cc/CCQuadCuller.cpp:
+        (WebCore::CCQuadCuller::CCQuadCuller):
+        (WebCore::appendQuadInternal):
+        (WebCore::CCQuadCuller::append):
+        (WebCore):
+        (WebCore::CCQuadCuller::appendSurface):
+        (WebCore::CCQuadCuller::appendReplica):
+        * platform/graphics/chromium/cc/CCQuadCuller.h:
+        (CCQuadCuller):
+        * platform/graphics/chromium/cc/CCRenderPass.cpp:
+        (WebCore::CCRenderPass::appendQuadsForRenderSurfaceLayer):
+        * platform/graphics/chromium/cc/CCRenderPass.h:
+        (CCRenderPass):
+
 2012-04-09  Andreas Kling  <kling@webkit.org>
 
         Make CSSValuePool share values globally.
index d21a4e0..9652fb4 100644 (file)
@@ -296,24 +296,21 @@ bool CCLayerTreeHostImpl::calculateRenderPasses(CCRenderPassList& passes, CCLaye
     CCLayerIteratorType end = CCLayerIteratorType::end(&renderSurfaceLayerList);
     for (CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); it != end; ++it) {
         CCRenderSurface* renderSurface = it.targetRenderSurfaceLayer()->renderSurface();
+        CCRenderPass* pass = surfacePassMap.get(renderSurface);
 
         if (it.representsItself())
             occlusionTracker.enterTargetRenderSurface(renderSurface);
-        else if (it.representsTargetRenderSurface())
+        else if (it.representsTargetRenderSurface()) {
             occlusionTracker.finishedTargetRenderSurface(*it, renderSurface);
-        else
-            occlusionTracker.leaveToTargetRenderSurface(renderSurface);
-
-        if (it.representsTargetRenderSurface())
             continue;
-        if (it->visibleLayerRect().isEmpty())
+        } else {
+            pass->appendQuadsForRenderSurfaceLayer(*it, &occlusionTracker);
+            occlusionTracker.leaveToTargetRenderSurface(renderSurface);
             continue;
+        }
 
-        CCRenderPass* pass = surfacePassMap.get(renderSurface);
-        if (it.representsContributingRenderSurface()) {
-            pass->appendQuadsForRenderSurfaceLayer(*it);
+        if (it->visibleLayerRect().isEmpty())
             continue;
-        }
 
         it->willDraw(m_layerRenderer.get());
 
index cf76bdf..aedb707 100644 (file)
@@ -141,6 +141,7 @@ void CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::leaveToTargetRenderSu
 
     const RenderSurfaceType* oldTarget = m_stack[lastIndex].surface;
     Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion<RenderSurfaceType>(oldTarget, m_stack[lastIndex].occlusionInTarget, oldTarget->originTransform());
+    // FIXME: The replica can occlude things too.
 
     if (surfaceWillBeAtTopAfterPop) {
         // Merge the top of the stack down.
@@ -335,6 +336,46 @@ IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContentR
 }
 
 template<typename LayerType, typename RenderSurfaceType>
+IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::unoccludedContributingSurfaceContentRect(const LayerType* layer, bool forReplica, const IntRect& contentRect) const
+{
+    ASSERT(!m_stack.isEmpty());
+    // This should be called while the contributing render surface is still considered the current target in the occlusion tracker.
+    ASSERT(layer->targetRenderSurface() == m_stack.last().surface);
+
+    // A contributing surface doesn't get occluded by things inside its own surface, so only things outside the surface can occlude it. That occlusion is
+    // found just below the top of the stack (if it exists).
+    if (m_stack.size() < 2)
+        return contentRect;
+    if (contentRect.isEmpty())
+        return contentRect;
+
+    RenderSurfaceType* surface = layer->renderSurface();
+    const StackObject& secondLast = m_stack[m_stack.size() - 2];
+
+    IntRect surfaceClipRect = surface->clipRect();
+    if (surfaceClipRect.isEmpty()) {
+        const RenderSurfaceType* targetSurface = secondLast.surface;
+        surfaceClipRect = intersection(targetSurface->contentRect(), enclosingIntRect(surface->drawableContentRect()));
+    }
+
+    const TransformationMatrix& transformToScreen = forReplica ? surface->replicaScreenSpaceTransform() : surface->screenSpaceTransform();
+    const TransformationMatrix& transformToTarget = forReplica ? surface->replicaOriginTransform() : surface->originTransform();
+
+    IntRect unoccludedInScreen = contentRect;
+    if (surfaceTransformsToScreenKnown(surface))
+        unoccludedInScreen = computeUnoccludedContentRect(contentRect, transformToScreen, m_scissorRectInScreenSpace, secondLast.occlusionInScreen);
+
+    if (unoccludedInScreen.isEmpty())
+        return unoccludedInScreen;
+
+    IntRect unoccludedInTarget = contentRect;
+    if (surfaceTransformsToTargetKnown(surface))
+        unoccludedInTarget = computeUnoccludedContentRect(contentRect, transformToTarget, surfaceClipRect, secondLast.occlusionInTarget);
+
+    return intersection(unoccludedInScreen, unoccludedInTarget);
+}
+
+template<typename LayerType, typename RenderSurfaceType>
 IntRect CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerScissorRectInTargetSurface(const LayerType* layer) const
 {
     const RenderSurfaceType* targetSurface = m_stack.last().surface;
@@ -352,6 +393,7 @@ template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::leav
 template void CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::markOccludedBehindLayer(const LayerChromium*);
 template bool CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::occluded(const LayerChromium*, const IntRect& contentRect) const;
 template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContentRect(const LayerChromium*, const IntRect& contentRect) const;
+template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::unoccludedContributingSurfaceContentRect(const LayerChromium*, bool forReplica, const IntRect& contentRect) const;
 template IntRect CCOcclusionTrackerBase<LayerChromium, RenderSurfaceChromium>::layerScissorRectInTargetSurface(const LayerChromium*) const;
 
 template CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::CCOcclusionTrackerBase(IntRect scissorRectInScreenSpace, bool recordMetricsForFrame);
@@ -361,6 +403,7 @@ template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::leaveToTarge
 template void CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::markOccludedBehindLayer(const CCLayerImpl*);
 template bool CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::occluded(const CCLayerImpl*, const IntRect& contentRect) const;
 template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContentRect(const CCLayerImpl*, const IntRect& contentRect) const;
+template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::unoccludedContributingSurfaceContentRect(const CCLayerImpl*, bool forReplica, const IntRect& contentRect) const;
 template IntRect CCOcclusionTrackerBase<CCLayerImpl, CCRenderSurface>::layerScissorRectInTargetSurface(const CCLayerImpl*) const;
 
 
index 6b2b6e1..a5934e1 100644 (file)
@@ -67,6 +67,10 @@ public:
     // Gives an unoccluded sub-rect of |contentRect| in the content space of the layer. Used when considering occlusion for a layer that paints/draws something.
     IntRect unoccludedContentRect(const LayerType*, const IntRect& contentRect) const;
 
+    // Gives an unoccluded sub-rect of |contentRect| in the content space of the surface owned by |layer|. Used when considering occlusion for a surface
+    // that is rendering into another target surface.
+    IntRect unoccludedContributingSurfaceContentRect(const LayerType*, bool forReplica, const IntRect& contentRect) const;
+
     // Report operations for recording overdraw metrics.
     CCOverdrawMetrics& overdrawMetrics() const { return *m_overdrawMetrics.get(); }
 
index 4701080..94d1d67 100644 (file)
@@ -40,29 +40,46 @@ using namespace std;
 
 namespace WebCore {
 
-CCQuadCuller::CCQuadCuller(CCQuadList& quadList, CCLayerImpl* layer, CCOcclusionTrackerImpl* occlusionTracker)
+CCQuadCuller::CCQuadCuller(CCQuadList& quadList, CCLayerImpl* layer, const CCOcclusionTrackerImpl* occlusionTracker)
     : m_quadList(quadList)
     , m_layer(layer)
     , m_occlusionTracker(occlusionTracker)
 {
 }
 
-bool CCQuadCuller::append(PassOwnPtr<CCDrawQuad> passDrawQuad)
+static inline bool appendQuadInternal(PassOwnPtr<CCDrawQuad> passDrawQuad, const IntRect& culledRect, CCQuadList& quadList, const CCOcclusionTrackerImpl& occlusionTracker)
 {
     OwnPtr<CCDrawQuad> drawQuad(passDrawQuad);
-    IntRect culledRect = m_occlusionTracker->unoccludedContentRect(m_layer, drawQuad->quadRect());
     bool keepQuad = !culledRect.isEmpty();
     if (keepQuad)
         drawQuad->setQuadVisibleRect(culledRect);
 
-    m_occlusionTracker->overdrawMetrics().didCullForDrawing(drawQuad->quadTransform(), drawQuad->quadRect(), culledRect);
-    m_occlusionTracker->overdrawMetrics().didDraw(drawQuad->quadTransform(), culledRect, drawQuad->opaqueRect());
+    occlusionTracker.overdrawMetrics().didCullForDrawing(drawQuad->quadTransform(), drawQuad->quadRect(), culledRect);
+    occlusionTracker.overdrawMetrics().didDraw(drawQuad->quadTransform(), culledRect, drawQuad->opaqueRect());
 
     // Release the quad after we're done using it.
     if (keepQuad)
-        m_quadList.append(drawQuad.release());
+        quadList.append(drawQuad.release());
     return keepQuad;
 }
 
+bool CCQuadCuller::append(PassOwnPtr<CCDrawQuad> passDrawQuad)
+{
+    IntRect culledRect = m_occlusionTracker->unoccludedContentRect(m_layer, passDrawQuad->quadRect());
+    return appendQuadInternal(passDrawQuad, culledRect, m_quadList, *m_occlusionTracker);
+}
+
+bool CCQuadCuller::appendSurface(PassOwnPtr<CCDrawQuad> passDrawQuad)
+{
+    IntRect culledRect = m_occlusionTracker->unoccludedContributingSurfaceContentRect(m_layer, false, passDrawQuad->quadRect());
+    return appendQuadInternal(passDrawQuad, culledRect, m_quadList, *m_occlusionTracker);
+}
+
+bool CCQuadCuller::appendReplica(PassOwnPtr<CCDrawQuad> passDrawQuad)
+{
+    IntRect culledRect = m_occlusionTracker->unoccludedContributingSurfaceContentRect(m_layer, true, passDrawQuad->quadRect());
+    return appendQuadInternal(passDrawQuad, culledRect, m_quadList, *m_occlusionTracker);
+}
+
 } // namespace WebCore
 #endif // USE(ACCELERATED_COMPOSITING)
index 3435b72..5db0a98 100644 (file)
@@ -36,15 +36,17 @@ class CCQuadCuller {
 public:
     // Passing 0 for CCOverdrawCounts* is valid, and disable the extra computation
     // done to estimate over draw statistics.
-    CCQuadCuller(CCQuadList&, CCLayerImpl*, CCOcclusionTrackerImpl*);
+    CCQuadCuller(CCQuadList&, CCLayerImpl*, const CCOcclusionTrackerImpl*);
 
     // Returns true if the quad is added to the list, and false if the quad is entirely culled.
     virtual bool append(PassOwnPtr<CCDrawQuad> passDrawQuad);
+    virtual bool appendSurface(PassOwnPtr<CCDrawQuad> passDrawQuad);
+    virtual bool appendReplica(PassOwnPtr<CCDrawQuad> passDrawQuad);
 
 private:
     CCQuadList& m_quadList;
     CCLayerImpl* m_layer;
-    CCOcclusionTrackerImpl* m_occlusionTracker;
+    const CCOcclusionTrackerImpl* m_occlusionTracker;
 };
 
 }
index 96b7608..61f1fdd 100644 (file)
@@ -66,18 +66,20 @@ void CCRenderPass::appendQuadsForLayer(CCLayerImpl* layer, CCOcclusionTrackerImp
     m_sharedQuadStateList.append(sharedQuadState.release());
 }
 
-void CCRenderPass::appendQuadsForRenderSurfaceLayer(CCLayerImpl* layer)
+void CCRenderPass::appendQuadsForRenderSurfaceLayer(CCLayerImpl* layer, CCOcclusionTrackerImpl* occlusionTracker)
 {
     // FIXME: render surface layers should be a CCLayerImpl-derived class and
     // not be handled specially here.
+    CCQuadCuller quadCuller(m_quadList, layer, occlusionTracker);
+
     CCRenderSurface* surface = layer->renderSurface();
     OwnPtr<CCSharedQuadState> sharedQuadState = surface->createSharedQuadState();
     if (layer->hasDebugBorders()) {
         Color color(debugSurfaceBorderColorRed, debugSurfaceBorderColorGreen, debugSurfaceBorderColorBlue, debugSurfaceBorderAlpha);
-        m_quadList.append(CCDebugBorderDrawQuad::create(sharedQuadState.get(), surface->contentRect(), color, debugSurfaceBorderWidth));
+        quadCuller.appendSurface(CCDebugBorderDrawQuad::create(sharedQuadState.get(), surface->contentRect(), color, debugSurfaceBorderWidth));
     }
     bool isReplica = false;
-    m_quadList.append(CCRenderSurfaceDrawQuad::create(sharedQuadState.get(), surface->contentRect(), layer, surfaceDamageRect(), isReplica));
+    quadCuller.appendSurface(CCRenderSurfaceDrawQuad::create(sharedQuadState.get(), surface->contentRect(), layer, surfaceDamageRect(), isReplica));
     m_sharedQuadStateList.append(sharedQuadState.release());
 
     // Add replica after the surface so that it appears below the surface.
@@ -85,10 +87,10 @@ void CCRenderPass::appendQuadsForRenderSurfaceLayer(CCLayerImpl* layer)
         OwnPtr<CCSharedQuadState> sharedQuadState = surface->createReplicaSharedQuadState();
         if (layer->hasDebugBorders()) {
             Color color(debugReplicaBorderColorRed, debugReplicaBorderColorGreen, debugReplicaBorderColorBlue, debugSurfaceBorderAlpha);
-            m_quadList.append(CCDebugBorderDrawQuad::create(sharedQuadState.get(), surface->contentRect(), color, debugSurfaceBorderWidth));
+            quadCuller.appendReplica(CCDebugBorderDrawQuad::create(sharedQuadState.get(), surface->contentRect(), color, debugSurfaceBorderWidth));
         }
         bool isReplica = true;
-        m_quadList.append(CCRenderSurfaceDrawQuad::create(sharedQuadState.get(), surface->contentRect(), layer, surfaceDamageRect(), isReplica));
+        quadCuller.appendReplica(CCRenderSurfaceDrawQuad::create(sharedQuadState.get(), surface->contentRect(), layer, surfaceDamageRect(), isReplica));
         m_sharedQuadStateList.append(sharedQuadState.release());
     }
 }
index 59901c8..9f3a7e7 100644 (file)
@@ -55,7 +55,7 @@ public:
     static PassOwnPtr<CCRenderPass> create(CCRenderSurface*);
 
     void appendQuadsForLayer(CCLayerImpl*, CCOcclusionTrackerImpl*, bool& usedCheckerboard);
-    void appendQuadsForRenderSurfaceLayer(CCLayerImpl*);
+    void appendQuadsForRenderSurfaceLayer(CCLayerImpl*, CCOcclusionTrackerImpl*);
 
     const CCQuadList& quadList() const { return m_quadList; }
     CCRenderSurface* targetSurface() const { return m_targetSurface; }
index 7041655..bd5ade9 100644 (file)
@@ -1,3 +1,31 @@
+2012-04-09  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Cull occluded surface quads
+        https://bugs.webkit.org/show_bug.cgi?id=81227
+
+        Reviewed by Adrienne Walker.
+
+        * tests/CCOcclusionTrackerTest.cpp:
+        (WebKitTests::CCOcclusionTrackerTest::TearDown):
+        (WebKitTests::CCOcclusionTrackerTest::createReplicaLayer):
+        (CCOcclusionTrackerTest):
+        (WebKitTests::CCOcclusionTrackerTest::setReplica):
+        (WebKitTests::CCOcclusionTrackerTestOverlappingSurfaceSiblings::runMyTest):
+        (WebKitTests::CCOcclusionTrackerTestOverlappingSurfaceSiblingsWithTwoTransforms::runMyTest):
+        (WebKitTests::CCOcclusionTrackerTestAnimationOpacity1OnMainThread::runMyTest):
+        (WebKitTests::CCOcclusionTrackerTestAnimationOpacity0OnMainThread::runMyTest):
+        (WebKitTests::CCOcclusionTrackerTestAnimationTranslateOnMainThread::runMyTest):
+        (WebKitTests::CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping::runMyTest):
+        (WebKitTests):
+        (CCOcclusionTrackerTestReplicaOccluded):
+        (WebKitTests::CCOcclusionTrackerTestReplicaOccluded::runMyTest):
+        (CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded):
+        (WebKitTests::CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded::runMyTest):
+        (CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently):
+        (WebKitTests::CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently::runMyTest):
+        (CCOcclusionTrackerTestSurfaceChildOfSurface):
+        (WebKitTests::CCOcclusionTrackerTestSurfaceChildOfSurface::runMyTest):
+
 2012-04-09  James Robinson  <jamesr@chromium.org>
 
         [chromium] Unreviewed build fix. OwnPtr<>::operator = breaks the build on clang, but not gcc :(
index b1a6f3a..c063531 100644 (file)
@@ -135,6 +135,7 @@ protected:
         m_root.clear();
         m_renderSurfaceLayerListChromium.clear();
         m_renderSurfaceLayerListImpl.clear();
+        m_replicaLayers.clear();
         CCLayerTreeHost::setNeedsFilterContext(false);
     }
 
@@ -187,6 +188,15 @@ protected:
         return layerPtr;
     }
 
+    typename Types::LayerType* createReplicaLayer(typename Types::LayerType* owningLayer, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds)
+    {
+        typename Types::ContentLayerPtrType layer(Types::createContentLayer());
+        typename Types::ContentLayerType* layerPtr = layer.get();
+        setProperties(layerPtr, transform, position, bounds);
+        setReplica(owningLayer, layer.release());
+        return layerPtr;
+    }
+
     typename Types::ContentLayerType* createDrawingSurface(typename Types::LayerType* parent, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds, bool opaque)
     {
         typename Types::ContentLayerType* layer = createDrawingLayer(parent, transform, position, bounds, opaque);
@@ -253,10 +263,22 @@ private:
         layer->setContentBounds(layer->bounds());
     }
 
+    void setReplica(LayerChromium* owningLayer, PassRefPtr<LayerChromium> layer)
+    {
+        owningLayer->setReplicaLayer(layer.get());
+        m_replicaLayers.append(layer);
+    }
+
+    void setReplica(CCLayerImpl* owningLayer, PassOwnPtr<CCLayerImpl> layer)
+    {
+        owningLayer->setReplicaLayer(layer);
+    }
+
     // These hold ownership of the layers for the duration of the test.
     typename Types::LayerPtrType m_root;
     Vector<RefPtr<LayerChromium> > m_renderSurfaceLayerListChromium;
     Vector<CCLayerImpl*> m_renderSurfaceLayerListImpl;
+    Vector<RefPtr<LayerChromium> > m_replicaLayers;
 };
 
 #define RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \
@@ -902,6 +924,10 @@ protected:
 
         occlusion.markOccludedBehindLayer(child2);
         occlusion.finishedTargetRenderSurface(child2, child2->renderSurface());
+
+        // There is nothing above child2's surface in the z-order.
+        EXPECT_EQ_RECT(IntRect(-10, 420, 70, 80), occlusion.unoccludedContributingSurfaceContentRect(child2, false, IntRect(-10, 420, 70, 80)));
+
         occlusion.leaveToTargetRenderSurface(parent->renderSurface());
         occlusion.enterTargetRenderSurface(child1->renderSurface());
         occlusion.markOccludedBehindLayer(layer1);
@@ -919,6 +945,10 @@ protected:
 
         occlusion.markOccludedBehindLayer(child1);
         occlusion.finishedTargetRenderSurface(child1, child1->renderSurface());
+
+        // child2's contents will occlude child1 below it.
+        EXPECT_EQ_RECT(IntRect(-10, 430, 10, 70), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(-10, 430, 80, 70)));
+
         occlusion.leaveToTargetRenderSurface(parent->renderSurface());
 
         EXPECT_EQ_RECT(IntRect(20, 20, 80, 80), occlusion.occlusionInScreenSpace().bounds());
@@ -981,7 +1011,7 @@ protected:
 
         typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100));
         typename Types::LayerType* child1 = this->createSurface(parent, child1Transform, FloatPoint(30, 20), IntSize(10, 10));
-        typename Types::LayerType* child2 = this->createSurface(parent, child2Transform, FloatPoint(20, 40), IntSize(10, 10));
+        typename Types::LayerType* child2 = this->createDrawingSurface(parent, child2Transform, FloatPoint(20, 40), IntSize(10, 10), false);
         typename Types::ContentLayerType* layer1 = this->createDrawingLayer(child1, this->identityMatrix, FloatPoint(-10, -20), IntSize(510, 510), true);
         typename Types::ContentLayerType* layer2 = this->createDrawingLayer(child2, this->identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true);
         this->calcDrawEtc(parent);
@@ -1005,7 +1035,12 @@ protected:
 
         occlusion.markOccludedBehindLayer(child2);
         occlusion.finishedTargetRenderSurface(child2, child2->renderSurface());
+
+        // There is nothing above child2's surface in the z-order.
+        EXPECT_EQ_RECT(IntRect(-10, 420, 70, 80), occlusion.unoccludedContributingSurfaceContentRect(child2, false, IntRect(-10, 420, 70, 80)));
+
         occlusion.leaveToTargetRenderSurface(parent->renderSurface());
+
         occlusion.enterTargetRenderSurface(child1->renderSurface());
         occlusion.markOccludedBehindLayer(layer1);
 
@@ -1022,6 +1057,12 @@ protected:
 
         occlusion.markOccludedBehindLayer(child1);
         occlusion.finishedTargetRenderSurface(child1, child1->renderSurface());
+
+        // child2's contents will occlude child1 below it.
+        EXPECT_EQ_RECT(IntRect(420, -20, 80, 90), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(420, -20, 80, 90)));
+        EXPECT_EQ_RECT(IntRect(490, -10, 10, 80), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(420, -10, 80, 90)));
+        EXPECT_EQ_RECT(IntRect(420, -20, 70, 10), occlusion.unoccludedContributingSurfaceContentRect(child1, false, IntRect(420, -20, 70, 90)));
+
         occlusion.leaveToTargetRenderSurface(parent->renderSurface());
 
         EXPECT_EQ_RECT(IntRect(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds());
@@ -1742,6 +1783,7 @@ protected:
         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* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 0), IntSize(50, 300), true);
 
         addOpacityTransitionToController(*layer->layerAnimationController(), 10, 0, 1, false);
         addOpacityTransitionToController(*surface->layerAnimationController(), 10, 0, 1, false);
@@ -1753,21 +1795,27 @@ protected:
 
         TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
 
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(topmost);
+        // This occlusion will affect all surfaces.
+        EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+
         occlusion.enterTargetRenderSurface(surface->renderSurface());
         occlusion.markOccludedBehindLayer(surfaceChild2);
-        EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        EXPECT_EQ_RECT(IntRect(100, 0, 150, 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)));
+        EXPECT_EQ_RECT(IntRect(200, 0, 50, 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 within the surface is lost when leaving the animating surface.
+        EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 300, 300)));
+
+        occlusion.leaveToTargetRenderSurface(parent->renderSurface());
         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)));
+        EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
     }
 };
 
@@ -1783,6 +1831,7 @@ protected:
         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* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 0), IntSize(50, 300), true);
 
         addOpacityTransitionToController(*layer->layerAnimationController(), 10, 1, 0, false);
         addOpacityTransitionToController(*surface->layerAnimationController(), 10, 1, 0, false);
@@ -1794,21 +1843,27 @@ protected:
 
         TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
 
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(topmost);
+        // This occlusion will affect all surfaces.
+        EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
+
         occlusion.enterTargetRenderSurface(surface->renderSurface());
         occlusion.markOccludedBehindLayer(surfaceChild2);
-        EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
+        EXPECT_EQ_RECT(IntRect(100, 0, 150, 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)));
+        EXPECT_EQ_RECT(IntRect(200, 0, 50, 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 within the surface is lost when leaving the animating surface.
+        EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 300, 300)));
+
+        occlusion.leaveToTargetRenderSurface(parent->renderSurface());
         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)));
+        EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
     }
 };
 
@@ -1888,8 +1943,12 @@ protected:
         EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300)));
 
         occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+
+        // The contributing |surface| is animating so it can't be occluded.
+        EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 300, 300)));
+
         occlusion.leaveToTargetRenderSurface(parent->renderSurface());
-        // The surface is moving in the screen and in its target, so all occlusion is lost when leaving it.
+        // The surface is moving in the screen and in its target, so all occlusion within the surface is lost when leaving it.
         EXPECT_EQ_RECT(IntRect(50, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)));
 
         occlusion.markOccludedBehindLayer(layer);
@@ -1957,7 +2016,7 @@ protected:
         surface->setOpaqueContentsRect(IntRect(0, 0, 400, 200));
         this->calcDrawEtc(parent);
 
-        TestCCOcclusionTrackerBase<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+        TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
 
         occlusion.enterTargetRenderSurface(surface->renderSurface());
         occlusion.markOccludedBehindLayer(surface);
@@ -1973,4 +2032,234 @@ protected:
 
 MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping);
 
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestReplicaOccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+        typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+        this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100));
+        typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100), true);
+        this->calcDrawEtc(parent);
+
+        TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+        occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000));
+
+        // This occludes the replica, but not the surface itself.
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(topmost);
+
+        EXPECT_EQ_RECT(IntRect(0, 100, 100, 100), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 100, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.enterTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surface);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 200), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+
+        // Surface is not occluded so it shouldn't think it is.
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+    }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaOccluded);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+        typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+        this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100));
+        typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 110), true);
+        this->calcDrawEtc(parent);
+
+        TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+        occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000));
+
+        // This occludes the surface, but not the entire surface's replica.
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(topmost);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 110), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 110), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.enterTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surface);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 110), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+
+        // Surface is occluded, but only the top 10px of the replica.
+        EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+        EXPECT_EQ_RECT(IntRect(0, 10, 100, 90), occlusion.unoccludedContributingSurfaceContentRect(surface, true, IntRect(0, 0, 100, 100)));
+    }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+        typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+        this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(0, 100), IntSize(100, 100));
+        typename Types::LayerType* overSurface = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(40, 100), true);
+        typename Types::LayerType* overReplica = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(50, 100), true);
+        this->calcDrawEtc(parent);
+
+        TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+        occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000));
+
+        // These occlude the surface and replica differently, so we can test each one.
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(overReplica);
+        occlusion.markOccludedBehindLayer(overSurface);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 50, 200), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 50, 200), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.enterTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surface);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 200), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+
+        // Surface and replica are occluded different amounts.
+        EXPECT_EQ_RECT(IntRect(40, 0, 60, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+        EXPECT_EQ_RECT(IntRect(50, 0, 50, 100), occlusion.unoccludedContributingSurfaceContentRect(surface, true, IntRect(0, 0, 100, 100)));
+    }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceChildOfSurface : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        // This test verifies that the surface cliprect does not end up empty and clip away the entire unoccluded rect.
+
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 200));
+        typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+        typename Types::LayerType* surfaceChild = this->createDrawingSurface(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), false);
+        typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 50), true);
+        this->calcDrawEtc(parent);
+
+        TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+        occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000));
+
+        // This occludes everything partially so we know occlusion is happening at all.
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(topmost);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 50), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 50), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.enterTargetRenderSurface(surfaceChild->renderSurface());
+        // surfaceChild is not opaque and does not occlude, so we have a non-empty unoccluded area on surface.
+        occlusion.markOccludedBehindLayer(surfaceChild);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 100, 50), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+        // The root layer always has a clipRect. So the parent of |surface| has a clipRect. However, the owning layer for |surface| does not
+        // mask to bounds, so it doesn't have a clipRect of its own. Thus the parent of |surfaceChild| exercises different code paths
+        // as its parent does not have a clipRect.
+
+        occlusion.finishedTargetRenderSurface(surfaceChild, surfaceChild->renderSurface());
+        // The surfaceChild's parent does not have a clipRect as it owns a render surface.
+        EXPECT_EQ_RECT(IntRect(0, 50, 100, 50), occlusion.unoccludedContributingSurfaceContentRect(surfaceChild, false, IntRect(0, 0, 100, 100)));
+
+        occlusion.leaveToTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surface);
+
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+        // The surface's parent does have a clipRect as it is the root layer.
+        EXPECT_EQ_RECT(IntRect(0, 50, 100, 50), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+    }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceChildOfSurface);
+
+template<class Types, bool opaqueLayers>
+class CCOcclusionTrackerTestSurfaceChildOfClippingSurface : public CCOcclusionTrackerTest<Types, opaqueLayers> {
+protected:
+    void runMyTest()
+    {
+        // This test verifies that the surface cliprect does not end up empty and clip away the entire unoccluded rect.
+
+        typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(80, 200));
+        typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), true);
+        typename Types::LayerType* surfaceChild = this->createDrawingSurface(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100), false);
+        typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 50), true);
+        this->calcDrawEtc(parent);
+
+        TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000));
+        occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000));
+
+        // This occludes everything partially so we know occlusion is happening at all.
+        occlusion.enterTargetRenderSurface(parent->renderSurface());
+        occlusion.markOccludedBehindLayer(topmost);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 80, 50), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 80, 50), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size());
+
+        occlusion.enterTargetRenderSurface(surfaceChild->renderSurface());
+        // surfaceChild is not opaque and does not occlude, so we have a non-empty unoccluded area on surface.
+        occlusion.markOccludedBehindLayer(surfaceChild);
+
+        EXPECT_EQ_RECT(IntRect(0, 0, 80, 50), occlusion.occlusionInScreenSpace().bounds());
+        EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size());
+        EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds());
+        EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size());
+
+        // The root layer always has a clipRect. So the parent of |surface| has a clipRect. However, the owning layer for |surface| does not
+        // mask to bounds, so it doesn't have a clipRect of its own. Thus the parent of |surfaceChild| exercises different code paths
+        // as its parent does not have a clipRect.
+
+        occlusion.finishedTargetRenderSurface(surfaceChild, surfaceChild->renderSurface());
+        // The surfaceChild's parent does not have a clipRect as it owns a render surface.
+        EXPECT_EQ_RECT(IntRect(0, 50, 80, 50), occlusion.unoccludedContributingSurfaceContentRect(surfaceChild, false, IntRect(0, 0, 100, 100)));
+
+        occlusion.leaveToTargetRenderSurface(surface->renderSurface());
+        occlusion.markOccludedBehindLayer(surface);
+
+        occlusion.finishedTargetRenderSurface(surface, surface->renderSurface());
+        // The surface's parent does have a clipRect as it is the root layer.
+        EXPECT_EQ_RECT(IntRect(0, 50, 80, 50), occlusion.unoccludedContributingSurfaceContentRect(surface, false, IntRect(0, 0, 100, 100)));
+    }
+};
+
+ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceChildOfClippingSurface);
+
 } // namespace