From: commit-queue@webkit.org Date: Wed, 22 Feb 2012 07:59:51 +0000 (+0000) Subject: [Chromium] New CCOcclusionTracker class with tests X-Git-Tag: 070512121124~12199 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=cbf918010bac78736ca94c1bc7c67e165d3b4f80;p=profile%2Fivi%2Fwebkit-efl.git [Chromium] New CCOcclusionTracker class with tests https://bugs.webkit.org/show_bug.cgi?id=78549 Patch by Dana Jansens on 2012-02-21 Reviewed by James Robinson. Source/WebCore: Adds a CCOcclusionTrackerBase template class that is able to track occlusion of layers while traversing the layer tree from front-to-back, with typedefed versions for the main and impl threads. At each step of the front-to-back traversal, the class should be notified of changes to the current render target, and when done working with a layer, the layer is added the tracked occlusion. The class provides tests for checking if a rect in content space for a layer/surface is occluded by others in front of it. Unit tests: CCOcclusionTrackerTest.cpp * WebCore.gypi: * platform/graphics/chromium/cc/CCOcclusionTracker.cpp: Added. (WebCore): (WebCore::::enterTargetRenderSurface): (WebCore::::finishedTargetRenderSurface): (WebCore::transformSurfaceOpaqueRegion): (WebCore::::leaveToTargetRenderSurface): (WebCore::contentToScreenSpaceTransform): (WebCore::contentToTargetSurfaceTransform): (WebCore::computeOcclusionBehindLayer): (WebCore::::markOccludedBehindLayer): (WebCore::testContentRectOccluded): (WebCore::::occluded): (WebCore::::surfaceOccluded): (WebCore::rectSubtractRegion): (WebCore::computeUnoccludedContentRect): (WebCore::::unoccludedContentRect): (WebCore::::surfaceUnoccludedContentRect): (WebCore::::currentOcclusionInScreenSpace): (WebCore::::currentOcclusionInTargetSurface): * platform/graphics/chromium/cc/CCOcclusionTracker.h: Added. (WebCore): (CCOcclusionTrackerBase): (WebCore::CCOcclusionTrackerBase::CCOcclusionTrackerBase): (StackObject): Source/WebKit/chromium: * WebKit.gypi: * tests/CCLayerTreeHostCommonTest.cpp: (WebCore): * tests/CCOcclusionTrackerTest.cpp: Added. (WebCore): (WebCore::setLayerPropertiesForTesting): (LayerChromiumWithForcedDrawsContent): (WebCore::LayerChromiumWithForcedDrawsContent::LayerChromiumWithForcedDrawsContent): (WebCore::LayerChromiumWithForcedDrawsContent::drawsContent): (TestCCOcclusionTracker): (WebCore::TestCCOcclusionTracker::occlusionInScreenSpace): (WebCore::TestCCOcclusionTracker::occlusionInTargetSurface): (WebCore::TestCCOcclusionTracker::setOcclusionInScreenSpace): (WebCore::TestCCOcclusionTracker::setOcclusionInTargetSurface): (WebCore::TEST): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108453 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index d1f8a16..c5beead 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,49 @@ +2012-02-21 Dana Jansens + + [Chromium] New CCOcclusionTracker class with tests + https://bugs.webkit.org/show_bug.cgi?id=78549 + + Reviewed by James Robinson. + + Adds a CCOcclusionTrackerBase template class that is able to track occlusion + of layers while traversing the layer tree from front-to-back, with typedefed + versions for the main and impl threads. + + At each step of the front-to-back traversal, the class should be notified of + changes to the current render target, and when done working with a layer, the + layer is added the tracked occlusion. + + The class provides tests for checking if a rect in content space + for a layer/surface is occluded by others in front of it. + + Unit tests: CCOcclusionTrackerTest.cpp + + * WebCore.gypi: + * platform/graphics/chromium/cc/CCOcclusionTracker.cpp: Added. + (WebCore): + (WebCore::::enterTargetRenderSurface): + (WebCore::::finishedTargetRenderSurface): + (WebCore::transformSurfaceOpaqueRegion): + (WebCore::::leaveToTargetRenderSurface): + (WebCore::contentToScreenSpaceTransform): + (WebCore::contentToTargetSurfaceTransform): + (WebCore::computeOcclusionBehindLayer): + (WebCore::::markOccludedBehindLayer): + (WebCore::testContentRectOccluded): + (WebCore::::occluded): + (WebCore::::surfaceOccluded): + (WebCore::rectSubtractRegion): + (WebCore::computeUnoccludedContentRect): + (WebCore::::unoccludedContentRect): + (WebCore::::surfaceUnoccludedContentRect): + (WebCore::::currentOcclusionInScreenSpace): + (WebCore::::currentOcclusionInTargetSurface): + * platform/graphics/chromium/cc/CCOcclusionTracker.h: Added. + (WebCore): + (CCOcclusionTrackerBase): + (WebCore::CCOcclusionTrackerBase::CCOcclusionTrackerBase): + (StackObject): + 2012-02-21 Andreas Kling Cache family lists in CSSValuePool. diff --git a/Source/WebCore/WebCore.gypi b/Source/WebCore/WebCore.gypi index f850fa8..7420cc5 100644 --- a/Source/WebCore/WebCore.gypi +++ b/Source/WebCore/WebCore.gypi @@ -3280,6 +3280,8 @@ 'platform/graphics/chromium/cc/CCLayerTreeHost.h', 'platform/graphics/chromium/cc/CCLayerTreeHostImpl.cpp', 'platform/graphics/chromium/cc/CCLayerTreeHostImpl.h', + 'platform/graphics/chromium/cc/CCOcclusionTracker.cpp', + 'platform/graphics/chromium/cc/CCOcclusionTracker.h', 'platform/graphics/chromium/cc/CCPageScaleAnimation.cpp', 'platform/graphics/chromium/cc/CCPageScaleAnimation.h', 'platform/graphics/chromium/cc/CCPluginDrawQuad.cpp', diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.cpp b/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.cpp new file mode 100644 index 0000000..c5611ac --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "cc/CCOcclusionTracker.h" + +#include "LayerChromium.h" +#include "cc/CCLayerImpl.h" + +#include + +using namespace std; + +namespace WebCore { + +template +void CCOcclusionTrackerBase::enterTargetRenderSurface(const RenderSurfaceType* newTarget) +{ + if (!m_stack.isEmpty() && m_stack.last().surface == newTarget) + return; + + const RenderSurfaceType* oldTarget = m_stack.isEmpty() ? 0 : m_stack.last().surface; + const RenderSurfaceType* oldAncestorThatMovesPixels = !oldTarget ? 0 : oldTarget->nearestAncestorThatMovesPixels(); + const RenderSurfaceType* newAncestorThatMovesPixels = newTarget->nearestAncestorThatMovesPixels(); + + m_stack.append(StackObject()); + m_stack.last().surface = newTarget; + + // We copy the screen occlusion into the new RenderSurface subtree, but we never copy in the + // target occlusion, since we are looking at a new RenderSurface target. + + // If we are entering a subtree that is going to move pixels around, then the occlusion we've computed + // so far won't apply to the pixels we're drawing here in the same way. We discard the occlusion thus + // far to be safe, and ensure we don't cull any pixels that are moved such that they become visible. + bool enteringSubtreeThatMovesPixels = newAncestorThatMovesPixels && newAncestorThatMovesPixels != oldAncestorThatMovesPixels; + + bool copyScreenOcclusionForward = m_stack.size() > 1 && !enteringSubtreeThatMovesPixels; + if (copyScreenOcclusionForward) { + int lastIndex = m_stack.size() - 1; + m_stack[lastIndex].occlusionInScreen = m_stack[lastIndex - 1].occlusionInScreen; + } +} + +template +void CCOcclusionTrackerBase::finishedTargetRenderSurface(const LayerType* owningLayer, const RenderSurfaceType* finishedTarget) +{ + // FIXME: Remove the owningLayer parameter when we can get all the info from the surface. + ASSERT(owningLayer->renderSurface() == finishedTarget); + + // Make sure we know about the target surface. + enterTargetRenderSurface(finishedTarget); + + if (owningLayer->maskLayer() || finishedTarget->drawOpacity() < 1 || finishedTarget->filters().hasFilterThatAffectsOpacity()) { + m_stack.last().occlusionInScreen = Region(); + m_stack.last().occlusionInTarget = Region(); + } +} + +template +static inline Region transformSurfaceOpaqueRegion(const RenderSurfaceType* surface, const Region& region, const TransformationMatrix& transform) +{ + // Verify that rects within the |surface| will remain rects in its target surface after applying |transform|. If this is true, then + // apply |transform| to each rect within |region| in order to transform the entire Region. + + IntRect bounds = region.bounds(); + FloatRect centeredBounds(-bounds.width() / 2.0, -bounds.height() / 2.0, bounds.width(), bounds.height()); + FloatQuad transformedBoundsQuad = transform.mapQuad(FloatQuad(centeredBounds)); + if (!transformedBoundsQuad.isRectilinear()) + return Region(); + + Region transformedRegion; + + IntRect surfaceBounds = surface->contentRect(); + Vector rects = region.rects(); + Vector::const_iterator end = rects.end(); + for (Vector::const_iterator i = rects.begin(); i != end; ++i) { + FloatRect centeredOriginRect(-i->width() / 2.0 + i->x() - surfaceBounds.x(), -i->height() / 2.0 + i->y() - surfaceBounds.y(), i->width(), i->height()); + FloatRect transformedRect = transform.mapRect(FloatRect(centeredOriginRect)); + transformedRegion.unite(enclosedIntRect(transformedRect)); + } + return transformedRegion; +} + +template +void CCOcclusionTrackerBase::leaveToTargetRenderSurface(const RenderSurfaceType* newTarget) +{ + int lastIndex = m_stack.size() - 1; + bool surfaceWillBeAtTopAfterPop = m_stack.size() > 1 && m_stack[lastIndex - 1].surface == newTarget; + + // We merge the screen occlusion from the current RenderSurface subtree out to its parent target RenderSurface. + // The target occlusion can be merged out as well but needs to be transformed to the new target. + + const RenderSurfaceType* oldTarget = m_stack[lastIndex].surface; + Region oldTargetOcclusionInNewTarget = transformSurfaceOpaqueRegion(oldTarget, m_stack[lastIndex].occlusionInTarget, oldTarget->drawTransform()); + + if (surfaceWillBeAtTopAfterPop) { + // Merge the top of the stack down. + + m_stack[lastIndex - 1].occlusionInScreen.unite(m_stack[lastIndex].occlusionInScreen); + m_stack[lastIndex - 1].occlusionInTarget.unite(oldTargetOcclusionInNewTarget); + m_stack.removeLast(); + } else { + // Replace the top of the stack with the new pushed surface. Copy the occluded screen region to the top. + m_stack.last().surface = newTarget; + m_stack.last().occlusionInTarget = oldTargetOcclusionInNewTarget; + } +} + +template +static inline TransformationMatrix contentToScreenSpaceTransform(const LayerType* layer) +{ + IntSize boundsInLayerSpace = layer->bounds(); + IntSize boundsInContentSpace = layer->contentBounds(); + + TransformationMatrix transform = layer->screenSpaceTransform(); + + if (boundsInContentSpace.isEmpty()) + return transform; + + // Scale from content space to layer space + transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast(boundsInContentSpace.width()), + boundsInLayerSpace.height() / static_cast(boundsInContentSpace.height())); + + return transform; +} + +template +static inline TransformationMatrix contentToTargetSurfaceTransform(const LayerType* layer) +{ + IntSize boundsInLayerSpace = layer->bounds(); + IntSize boundsInContentSpace = layer->contentBounds(); + + TransformationMatrix transform = layer->drawTransform(); + + if (boundsInContentSpace.isEmpty()) + return transform; + + // Scale from content space to layer space + transform.scaleNonUniform(boundsInLayerSpace.width() / static_cast(boundsInContentSpace.width()), + boundsInLayerSpace.height() / static_cast(boundsInContentSpace.height())); + + // The draw transform expects the origin to be in the middle of the layer. + transform.translate(-boundsInContentSpace.width() / 2.0, -boundsInContentSpace.height() / 2.0); + + return transform; +} + +template +static inline Region computeOcclusionBehindLayer(const LayerType* layer, const TransformationMatrix& transform) +{ + Region opaqueRegion; + + FloatQuad unoccludedQuad = transform.mapQuad(FloatQuad(layer->visibleLayerRect())); + bool isPaintedAxisAligned = unoccludedQuad.isRectilinear(); + if (!isPaintedAxisAligned) + return opaqueRegion; + + if (layer->opaque()) + opaqueRegion = enclosedIntRect(unoccludedQuad.boundingBox()); + // FIXME: Capture opaque paints: else opaqueRegion = layer->opaqueContentsRegion(transform); + return opaqueRegion; +} + +template +void CCOcclusionTrackerBase::markOccludedBehindLayer(const LayerType* layer) +{ + ASSERT(!m_stack.isEmpty()); + ASSERT(layer->targetRenderSurface() == m_stack.last().surface); + + if (layer->drawOpacity() != 1) + return; + + TransformationMatrix contentToScreenSpace = contentToScreenSpaceTransform(layer); + TransformationMatrix contentToTargetSurface = contentToTargetSurfaceTransform(layer); + + m_stack.last().occlusionInScreen.unite(computeOcclusionBehindLayer(layer, contentToScreenSpace)); + m_stack.last().occlusionInTarget.unite(computeOcclusionBehindLayer(layer, contentToTargetSurface)); +} + +static inline bool testContentRectOccluded(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const Region& occlusion) +{ + FloatQuad transformedQuad = contentSpaceTransform.mapQuad(FloatQuad(contentRect)); + return occlusion.contains(transformedQuad.enclosingBoundingBox()); +} + +template +bool CCOcclusionTrackerBase::occluded(const LayerType* layer, const IntRect& contentRect) const +{ + if (m_stack.isEmpty()) + return false; + + ASSERT(layer->targetRenderSurface() == m_stack.last().surface); + + if (testContentRectOccluded(contentRect, contentToScreenSpaceTransform(layer), m_stack.last().occlusionInScreen)) + return true; + if (testContentRectOccluded(contentRect, contentToTargetSurfaceTransform(layer), m_stack.last().occlusionInTarget)) + return true; + return false; +} + +template +bool CCOcclusionTrackerBase::surfaceOccluded(const LayerType* layer, const IntRect& surfaceContentRect) const +{ + // A surface is not occluded by layers drawing into itself, so we need to use occlusion from one spot down on the stack. + if (m_stack.size() < 2) + return false; + + ASSERT(layer->renderSurface()); + ASSERT(layer->renderSurface() == m_stack.last().surface); + + TransformationMatrix surfaceContentToScreenSpace = contentToScreenSpaceTransform(layer); + + const StackObject& secondLast = m_stack[m_stack.size()-2]; + if (testContentRectOccluded(surfaceContentRect, surfaceContentToScreenSpace, secondLast.occlusionInScreen)) + return true; + return false; +} + +// Determines what portion of rect, if any, is unoccluded (not occluded by region). If +// the resulting unoccluded region is not rectangular, we return a rect containing it. +static inline IntRect rectSubtractRegion(const IntRect& rect, const Region& region) +{ + Region rectRegion(rect); + Region intersectRegion(intersect(region, rectRegion)); + + if (intersectRegion.isEmpty()) + return rect; + + rectRegion.subtract(intersectRegion); + IntRect boundsRect = rectRegion.bounds(); + return boundsRect; +} + +static IntRect computeUnoccludedContentRect(const IntRect& contentRect, const TransformationMatrix& contentSpaceTransform, const Region& occlusion) +{ + FloatQuad transformedQuad = contentSpaceTransform.mapQuad(FloatQuad(contentRect)); + if (!transformedQuad.isRectilinear()) + return contentRect; + // Take the enclosingIntRect at each step here, as we want to contain any unoccluded partial pixels in the resulting IntRect. + IntRect shrunkRect = rectSubtractRegion(enclosingIntRect(transformedQuad.boundingBox()), occlusion); + IntRect unoccludedRect = enclosingIntRect(contentSpaceTransform.inverse().mapRect(FloatRect(shrunkRect))); + // The use of enclosingIntRect, with floating point rounding, can give us a result that is not a sub-rect of contentRect, but our + // return value should be a sub-rect. + return intersection(unoccludedRect, contentRect); +} + +template +IntRect CCOcclusionTrackerBase::unoccludedContentRect(const LayerType* layer, const IntRect& contentRect) const +{ + if (m_stack.isEmpty()) + return contentRect; + + // 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(layer); + TransformationMatrix contentToTargetSurface = contentToTargetSurfaceTransform(layer); + + IntRect unoccludedInScreen = computeUnoccludedContentRect(contentRect, contentToScreenSpace, m_stack.last().occlusionInScreen); + IntRect unoccludedInTarget = computeUnoccludedContentRect(contentRect, contentToTargetSurface, m_stack.last().occlusionInTarget); + + return intersection(unoccludedInScreen, unoccludedInTarget); +} + +template +IntRect CCOcclusionTrackerBase::surfaceUnoccludedContentRect(const LayerType* layer, const IntRect& surfaceContentRect) const +{ + // A surface is not occluded by layers drawing into itself, so we need to use occlusion from one spot down on the stack. + if (m_stack.size() < 2) + return surfaceContentRect; + + ASSERT(layer->renderSurface()); + ASSERT(layer->renderSurface() == m_stack.last().surface); + + // 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(layer); + + const StackObject& secondLast = m_stack[m_stack.size()-2]; + IntRect unoccludedInScreen = computeUnoccludedContentRect(surfaceContentRect, contentToScreenSpace, secondLast.occlusionInScreen); + + return unoccludedInScreen; +} + +template +const Region& CCOcclusionTrackerBase::currentOcclusionInScreenSpace() const +{ + ASSERT(!m_stack.isEmpty()); + return m_stack.last().occlusionInScreen; +} + +template +const Region& CCOcclusionTrackerBase::currentOcclusionInTargetSurface() const +{ + ASSERT(!m_stack.isEmpty()); + return m_stack.last().occlusionInTarget; +} + + +// Declare the possible functions here for the linker. +template void CCOcclusionTrackerBase::enterTargetRenderSurface(const RenderSurfaceChromium* newTarget); +template void CCOcclusionTrackerBase::finishedTargetRenderSurface(const LayerChromium* owningLayer, const RenderSurfaceChromium* finishedTarget); +template void CCOcclusionTrackerBase::leaveToTargetRenderSurface(const RenderSurfaceChromium* newTarget); +template void CCOcclusionTrackerBase::markOccludedBehindLayer(const LayerChromium*); +template bool CCOcclusionTrackerBase::occluded(const LayerChromium*, const IntRect& contentRect) const; +template bool CCOcclusionTrackerBase::surfaceOccluded(const LayerChromium*, const IntRect& surfaceContentRect) const; +template IntRect CCOcclusionTrackerBase::unoccludedContentRect(const LayerChromium*, const IntRect& contentRect) const; +template IntRect CCOcclusionTrackerBase::surfaceUnoccludedContentRect(const LayerChromium*, const IntRect& surfaceContentRect) const; +template const Region& CCOcclusionTrackerBase::currentOcclusionInScreenSpace() const; +template const Region& CCOcclusionTrackerBase::currentOcclusionInTargetSurface() const; + +template void CCOcclusionTrackerBase::enterTargetRenderSurface(const CCRenderSurface* newTarget); +template void CCOcclusionTrackerBase::finishedTargetRenderSurface(const CCLayerImpl* owningLayer, const CCRenderSurface* finishedTarget); +template void CCOcclusionTrackerBase::leaveToTargetRenderSurface(const CCRenderSurface* newTarget); +template void CCOcclusionTrackerBase::markOccludedBehindLayer(const CCLayerImpl*); +template bool CCOcclusionTrackerBase::occluded(const CCLayerImpl*, const IntRect& contentRect) const; +template bool CCOcclusionTrackerBase::surfaceOccluded(const CCLayerImpl*, const IntRect& surfaceContentRect) const; +template IntRect CCOcclusionTrackerBase::unoccludedContentRect(const CCLayerImpl*, const IntRect& contentRect) const; +template IntRect CCOcclusionTrackerBase::surfaceUnoccludedContentRect(const CCLayerImpl*, const IntRect& surfaceContentRect) const; +template const Region& CCOcclusionTrackerBase::currentOcclusionInScreenSpace() const; +template const Region& CCOcclusionTrackerBase::currentOcclusionInTargetSurface() const; + + +} // namespace WebCore +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.h b/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.h new file mode 100644 index 0000000..d1c833e --- /dev/null +++ b/Source/WebCore/platform/graphics/chromium/cc/CCOcclusionTracker.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CCOcclusionTracker_h +#define CCOcclusionTracker_h + +#include "FloatQuad.h" +#include "Region.h" +#include "TransformationMatrix.h" + +namespace WebCore { +class CCLayerImpl; +class CCRenderSurface; +class LayerChromium; +class RenderSurfaceChromium; + +// This class is used to track occlusion of layers while traversing them in a front-to-back order. As each layer is visited, one of the +// methods in this class is called to notify it about the current target surface. +// Then, occlusion in the content space of the current layer may be queried, via methods such as occluded() and unoccludedContentRect(). +// If the current layer owns a RenderSurface, then occlusion on that RenderSurface may also be queried via surfaceOccluded() and surfaceUnoccludedContentRect(). +// Finally, once finished with the layer, occlusion behind the layer should be marked by calling markOccludedBehindLayer(). +template +class CCOcclusionTrackerBase { +public: + CCOcclusionTrackerBase() { } + + // Called when visiting a layer representing itself. If the target was not already current, then this indicates we have entered a new surface subtree. + void enterTargetRenderSurface(const RenderSurfaceType* newTarget); + + // Called when visiting a layer representing a target surface. This indicates we have visited all the layers within the surface, and we may + // perform any surface-wide operations. + void finishedTargetRenderSurface(const LayerType*, const RenderSurfaceType* finishedTarget); + + // Called when visiting a layer representing a contributing surface. This indicates that we are leaving our current surface, and + // entering the new one. We then perform any operations required for merging results from the child subtree into its parent. + void leaveToTargetRenderSurface(const RenderSurfaceType* newTarget); + + // Add the layer's occlusion to the tracked state. + void markOccludedBehindLayer(const LayerType*); + + // Returns true if the given rect in content space for the layer is fully occluded in either screen space or the layer's target surface. + bool occluded(const LayerType*, const IntRect& contentRect) const; + // 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; + + // Returns true if the given rect in content space for the RenderSurface owned by the layer is fully occluded in either screen space or the layer's target surface. + bool surfaceOccluded(const LayerType*, const IntRect& contentRect) const; + // Gives an unoccluded sub-rect of |contentRect| in the content space of the RenderSurface owned by the layer. Used when considering occlusion for a target surface. + IntRect surfaceUnoccludedContentRect(const LayerType*, const IntRect& contentRect) const; + + // FIXME: Remove these in future, they are to make CLs for transitioning to this easier. + const Region& currentOcclusionInScreenSpace() const; + const Region& currentOcclusionInTargetSurface() const; + +protected: + struct StackObject { + const RenderSurfaceType* surface; + Region occlusionInScreen; + Region occlusionInTarget; + }; + + // The stack holds occluded regions for subtrees in the RenderSurface-Layer tree, so that when we leave a subtree we may + // apply a mask to it, but not to the parts outside the subtree. + // - The first time we see a new subtree under a target, we add that target to the top of the stack. This can happen as a layer representing itself, or as a target surface. + // - When we visit a target surface, we apply its mask to its subtree, which is at the top of the stack. + // - When we visit a layer representing itself, we add its occlusion to the current subtree, which is at the top of the stack. + // - When we visit a layer representing a contributing surface, the current target will never be the top of the stack since we just came from the contributing surface. + // We merge the occlusion at the top of the stack with the new current subtree. This new target is pushed onto the stack if not already there. + Vector m_stack; + +private: + WTF_MAKE_NONCOPYABLE(CCOcclusionTrackerBase); +}; + +typedef CCOcclusionTrackerBase CCOcclusionTracker; +typedef CCOcclusionTrackerBase CCOcclusionTrackerImpl; + +} +#endif // CCOcclusionTracker_h diff --git a/Source/WebKit/chromium/ChangeLog b/Source/WebKit/chromium/ChangeLog index 0f8275a..d3a16dc 100644 --- a/Source/WebKit/chromium/ChangeLog +++ b/Source/WebKit/chromium/ChangeLog @@ -1,3 +1,26 @@ +2012-02-21 Dana Jansens + + [Chromium] New CCOcclusionTracker class with tests + https://bugs.webkit.org/show_bug.cgi?id=78549 + + Reviewed by James Robinson. + + * WebKit.gypi: + * tests/CCLayerTreeHostCommonTest.cpp: + (WebCore): + * tests/CCOcclusionTrackerTest.cpp: Added. + (WebCore): + (WebCore::setLayerPropertiesForTesting): + (LayerChromiumWithForcedDrawsContent): + (WebCore::LayerChromiumWithForcedDrawsContent::LayerChromiumWithForcedDrawsContent): + (WebCore::LayerChromiumWithForcedDrawsContent::drawsContent): + (TestCCOcclusionTracker): + (WebCore::TestCCOcclusionTracker::occlusionInScreenSpace): + (WebCore::TestCCOcclusionTracker::occlusionInTargetSurface): + (WebCore::TestCCOcclusionTracker::setOcclusionInScreenSpace): + (WebCore::TestCCOcclusionTracker::setOcclusionInTargetSurface): + (WebCore::TEST): + 2012-02-21 MORITA Hajime INPUT_SPEECH should be implemented as a PageSupplement. diff --git a/Source/WebKit/chromium/WebKit.gypi b/Source/WebKit/chromium/WebKit.gypi index 6e9a4d0..9aa5d2c 100644 --- a/Source/WebKit/chromium/WebKit.gypi +++ b/Source/WebKit/chromium/WebKit.gypi @@ -75,6 +75,7 @@ 'tests/CCLayerTreeHostImplTest.cpp', 'tests/CCLayerTreeHostTest.cpp', 'tests/CCLayerTreeTestCommon.h', + 'tests/CCOcclusionTrackerTest.cpp', 'tests/CCQuadCullerTest.cpp', 'tests/CCRenderSurfaceTest.cpp', 'tests/CCSchedulerStateMachineTest.cpp', diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp index b1fa729..d2e1cfe 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp @@ -28,7 +28,6 @@ #include "CCLayerTreeTestCommon.h" #include "LayerChromium.h" -#include "Region.h" #include "TransformationMatrix.h" #include @@ -36,12 +35,6 @@ using namespace WebCore; -#define EXPECT_EQ_RECT(a, b) \ - EXPECT_EQ(a.x(), b.x()); \ - EXPECT_EQ(a.y(), b.y()); \ - EXPECT_EQ(a.width(), b.width()); \ - EXPECT_EQ(a.height(), b.height()); - namespace { void setLayerPropertiesForTesting(LayerChromium* layer, const TransformationMatrix& transform, const TransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool preserves3D) @@ -602,174 +595,4 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces) // - test the other functions in CCLayerTreeHostCommon // -TEST(CCLayerTreeHostCommonTest, layerAddsSelfToOccludedRegion) -{ - // This tests that the right transforms are being used. - Region occluded; - const TransformationMatrix identityMatrix; - RefPtr parent = LayerChromium::create(); - RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); - setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); - - layer->setOpaque(true); - - Vector > renderSurfaceLayerList; - Vector > 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->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); - - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); - - occluded = Region(); - layer->addSelfToOccludedScreenSpace(occluded); - EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occluded.bounds()); - EXPECT_EQ(1u, occluded.rects().size()); -} - -TEST(CCLayerTreeHostCommonTest, layerAddsSelfToOccludedRegionWithRotation) -{ - // This tests that the right transforms are being used. - Region occluded; - const TransformationMatrix identityMatrix; - RefPtr parent = LayerChromium::create(); - RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); - - TransformationMatrix layerTransform; - layerTransform.translate(250, 250); - layerTransform.rotate(90); - layerTransform.translate(-250, -250); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); - setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); - - layer->setOpaque(true); - - Vector > renderSurfaceLayerList; - Vector > 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->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); - - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); - - occluded = Region(); - layer->addSelfToOccludedScreenSpace(occluded); - EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occluded.bounds()); - EXPECT_EQ(1u, occluded.rects().size()); -} - -TEST(CCLayerTreeHostCommonTest, layerAddsSelfToOccludedRegionWithTranslation) -{ - // This tests that the right transforms are being used. - Region occluded; - const TransformationMatrix identityMatrix; - RefPtr parent = LayerChromium::create(); - RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); - - TransformationMatrix layerTransform; - layerTransform.translate(20, 20); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); - setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); - - layer->setOpaque(true); - - Vector > renderSurfaceLayerList; - Vector > 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->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); - - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); - - occluded = Region(); - layer->addSelfToOccludedScreenSpace(occluded); - EXPECT_EQ_RECT(IntRect(50, 50, 50, 50), occluded.bounds()); - EXPECT_EQ(1u, occluded.rects().size()); -} - -TEST(CCLayerTreeHostCommonTest, layerAddsSelfToOccludedRegionWithRotatedSurface) -{ - // This tests that the right transforms are being used. - Region occluded; - const TransformationMatrix identityMatrix; - RefPtr parent = LayerChromium::create(); - RefPtr child = LayerChromium::create(); - RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(child); - child->addChild(layer); - - TransformationMatrix childTransform; - childTransform.translate(250, 250); - childTransform.rotate(90); - childTransform.translate(-250, -250); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); - setLayerPropertiesForTesting(child.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); - setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), false); - - child->setMasksToBounds(true); - layer->setOpaque(true); - - Vector > renderSurfaceLayerList; - Vector > 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->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); - - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); - - occluded = Region(); - layer->addSelfToOccludedScreenSpace(occluded); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occluded.bounds()); - EXPECT_EQ(1u, occluded.rects().size()); - - /* Justification for the above opaque rect from |layer|: - 100 - +---------------------+ +---------------------+ - | | | |30 Visible region of |layer|: ///// - | 30 | rotate(90) | | - | 30 + ---------------------------------+ | +---------------------------------+ - 100 | | 10 | | ==> | | |10 | - | |10+---------------------------------+ | +---------------------------------+ | - | | | | | | | | |///////////////| 420 | | - | | | | | | | | |///////////////|60 | | - | | | | | | | | |///////////////| | | - +----|--|-------------+ | | +--|--|---------------+ | | - | | | | 20|10| 70 | | - | | | | | | | | - | | | |500 | | | | - | | | | | | | | - | | | | | | | | - | | | | | | | | - | | | | | | |10| - +--|-------------------------------+ | | +------------------------------|--+ - | | | 490 | - +---------------------------------+ +---------------------------------+ - 500 500 - */ -} - } // namespace diff --git a/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp b/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp new file mode 100644 index 0000000..7e7310b --- /dev/null +++ b/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp @@ -0,0 +1,1028 @@ +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "cc/CCOcclusionTracker.h" + +#include "FilterOperations.h" +#include "LayerChromium.h" +#include "Region.h" +#include "TransformationMatrix.h" +#include "cc/CCLayerTreeHostCommon.h" + +#include +#include + +using namespace WebCore; + +#define EXPECT_EQ_RECT(a, b) \ + EXPECT_EQ(a.x(), b.x()); \ + EXPECT_EQ(a.y(), b.y()); \ + EXPECT_EQ(a.width(), b.width()); \ + EXPECT_EQ(a.height(), b.height()); + +namespace { + +void setLayerPropertiesForTesting(LayerChromium* layer, const TransformationMatrix& transform, const TransformationMatrix& sublayerTransform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool opaque) +{ + layer->setTransform(transform); + layer->setSublayerTransform(sublayerTransform); + layer->setAnchorPoint(anchor); + layer->setPosition(position); + layer->setBounds(bounds); + layer->setOpaque(opaque); +} + +class LayerChromiumWithForcedDrawsContent : public LayerChromium { +public: + LayerChromiumWithForcedDrawsContent() + : LayerChromium() + { + } + + virtual bool drawsContent() const { return true; } +}; + +// A subclass to expose the total current occlusion. +class TestCCOcclusionTracker : public CCOcclusionTracker { +public: + Region occlusionInScreenSpace() const { return CCOcclusionTracker::m_stack.last().occlusionInScreen; } + Region occlusionInTargetSurface() const { return CCOcclusionTracker::m_stack.last().occlusionInTarget; } + + void setOcclusionInScreenSpace(const Region& region) { CCOcclusionTracker::m_stack.last().occlusionInScreen = region; } + void setOcclusionInTargetSurface(const Region& region) { CCOcclusionTracker::m_stack.last().occlusionInTarget = region; } +}; + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegion) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(layer); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(parent->renderSurface()); + occlusion.markOccludedBehindLayer(layer.get()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(31, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 31, 70, 70))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(30, 30, 70, 70)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 30, 1, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 29, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 29, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 29, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(100, 30, 1, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 31, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 31, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 31, 70, 70))); +} + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegionWithRotation) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(layer); + + TransformationMatrix layerTransform; + layerTransform.translate(250, 250); + layerTransform.rotate(90); + layerTransform.translate(-250, -250); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(parent->renderSurface()); + occlusion.markOccludedBehindLayer(layer.get()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(31, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 31, 70, 70))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(30, 30, 70, 70)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 30, 1, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 29, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 29, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 29, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(100, 30, 1, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 31, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 31, 70, 70), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 31, 70, 70))); +} + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegionWithTranslation) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(layer); + + TransformationMatrix layerTransform; + layerTransform.translate(20, 20); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(parent->renderSurface()); + occlusion.markOccludedBehindLayer(layer.get()); + EXPECT_EQ_RECT(IntRect(50, 50, 50, 50), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(50, 50, 50, 50), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(50, 50, 50, 50))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(49, 50, 50, 50))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(50, 49, 50, 50))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(51, 50, 50, 50))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(50, 51, 50, 50))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(50, 50, 50, 50)).isEmpty()); + EXPECT_EQ_RECT(IntRect(49, 50, 1, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(49, 50, 50, 50))); + EXPECT_EQ_RECT(IntRect(49, 49, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(49, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(50, 49, 50, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(50, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(51, 49, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(51, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(100, 50, 1, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(51, 50, 50, 50))); + EXPECT_EQ_RECT(IntRect(51, 51, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(51, 51, 50, 50))); + EXPECT_EQ_RECT(IntRect(50, 100, 50, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(50, 51, 50, 50))); + EXPECT_EQ_RECT(IntRect(49, 51, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(49, 51, 50, 50))); +} + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegionWithRotatedSurface) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr child = LayerChromium::create(); + RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(layer); + + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); + setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + + child->setMasksToBounds(true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(child->renderSurface()); + occlusion.markOccludedBehindLayer(layer.get()); + + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 430, 61, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 430, 60, 71))); + + occlusion.markOccludedBehindLayer(child.get()); + occlusion.finishedTargetRenderSurface(child.get(), child->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 39, 70, 60))); + + + /* Justification for the above occlusion from |layer|: + 100 + +---------------------+ +---------------------+ + | | | |30 Visible region of |layer|: ///// + | 30 | rotate(90) | | + | 30 + ---------------------------------+ | +---------------------------------+ + 100 | | 10 | | ==> | | |10 | + | |10+---------------------------------+ | +---------------------------------+ | + | | | | | | | | |///////////////| 420 | | + | | | | | | | | |///////////////|60 | | + | | | | | | | | |///////////////| | | + +----|--|-------------+ | | +--|--|---------------+ | | + | | | | 20|10| 70 | | + | | | | | | | | + | | | |500 | | | | + | | | | | | | | + | | | | | | | | + | | | | | | | | + | | | | | | |10| + +--|-------------------------------+ | | +------------------------------|--+ + | | | 490 | + +---------------------------------+ +---------------------------------+ + 500 500 + */ +} + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegionWithSurfaceAlreadyOnStack) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr child = LayerChromium::create(); + RefPtr child2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(layer); + parent->addChild(child2); + + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); + setLayerPropertiesForTesting(layer.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + + // |child2| makes |parent|'s surface get considered by CCOcclusionTracker first, instead of |child|'s. This exercises different code in + // leaveToTargetRenderSurface, as the target surface has already been seen. + setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(60, 20), true); + + child->setMasksToBounds(true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(parent->renderSurface()); + occlusion.markOccludedBehindLayer(child2.get()); + + EXPECT_EQ_RECT(IntRect(30, 30, 60, 20), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 30, 60, 20), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + occlusion.enterTargetRenderSurface(child->renderSurface()); + occlusion.markOccludedBehindLayer(layer.get()); + + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(11, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 431, 60, 70))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(10, 430, 60, 70)).isEmpty()); + // This is the little piece not occluded by child2 + EXPECT_EQ_RECT(IntRect(9, 430, 1, 10), occlusion.unoccludedContentRect(child.get(), IntRect(9, 430, 60, 70))); + // This extends past both sides of child2, so it will be the original rect. + EXPECT_EQ_RECT(IntRect(9, 430, 60, 80), occlusion.unoccludedContentRect(child.get(), IntRect(9, 430, 60, 80))); + // This extends past two adjacent sides of child2, and should included the unoccluded parts of each side. + // This also demonstrates that the rect can be arbitrary and does not get clipped to the layer's visibleLayerRect(). + EXPECT_EQ_RECT(IntRect(-10, 430, 20, 70), occlusion.unoccludedContentRect(child.get(), IntRect(-10, 430, 60, 70))); + // This extends past three adjacent sides of child2, so it should contain the unoccluded parts of each side. The left + // and bottom edges are completely unoccluded for some row/column so we get back the original query rect. + EXPECT_EQ_RECT(IntRect(-10, 430, 60, 80), occlusion.unoccludedContentRect(child.get(), IntRect(-10, 430, 60, 80))); + EXPECT_EQ_RECT(IntRect(10, 429, 60, 1), occlusion.unoccludedContentRect(child.get(), IntRect(10, 429, 60, 70))); + EXPECT_EQ_RECT(IntRect(70, 430, 1, 70), occlusion.unoccludedContentRect(child.get(), IntRect(11, 430, 60, 70))); + EXPECT_EQ_RECT(IntRect(10, 500, 60, 1), occlusion.unoccludedContentRect(child.get(), IntRect(10, 431, 60, 70))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(10, 430, 60, 70), occlusion.surfaceUnoccludedContentRect(child.get(), IntRect(10, 430, 60, 70))); + + occlusion.markOccludedBehindLayer(child.get()); + // |child2| should get merged with the surface we are leaving now + occlusion.finishedTargetRenderSurface(child.get(), child->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + + Vector screen = occlusion.occlusionInScreenSpace().rects(); + Vector target = occlusion.occlusionInTargetSurface().rects(); + + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(90, 30, 10, 10), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 30, 70, 70))); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 30, 60, 10))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 30, 60, 10))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 29, 60, 10))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(31, 30, 60, 10))); + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 31, 60, 10))); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 39, 70, 60))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(30, 30, 60, 10)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 30, 1, 10), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 30, 60, 10))); + EXPECT_EQ_RECT(IntRect(30, 29, 60, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 29, 60, 10))); + EXPECT_EQ_RECT(IntRect(90, 30, 1, 10), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 30, 60, 10))); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(30, 31, 60, 10)).isEmpty()); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(30, 40, 70, 60)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 40, 1, 60), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 40, 70, 60))); + // This rect is mostly occluded by |child2|. + EXPECT_EQ_RECT(IntRect(90, 39, 10, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 39, 70, 60))); + // This rect extends past top/right ends of |child2|. + EXPECT_EQ_RECT(IntRect(30, 29, 70, 11), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 29, 70, 70))); + // This rect extends past left/right ends of |child2|. + EXPECT_EQ_RECT(IntRect(20, 39, 80, 60), occlusion.unoccludedContentRect(parent.get(), IntRect(20, 39, 80, 60))); + EXPECT_EQ_RECT(IntRect(100, 40, 1, 60), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 40, 70, 60))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 41, 70, 60))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.surfaceUnoccludedContentRect(parent.get(), IntRect(30, 40, 70, 60))); + + + /* Justification for the above occlusion from |layer|: + 100 + +---------------------+ +---------------------+ + | | | |30 Visible region of |layer|: ///// + | 30 | rotate(90) | 30 60 | |child2|: \\\\\ + | 30 + ------------+--------------------+ | 30 +------------+--------------------+ + 100 | | 10 | | | ==> | |\\\\\\\\\\\\| |10 | + | |10+----------|----------------------+ | +--|\\\\\\\\\\\\|-----------------+ | + | + ------------+ | | | | | +------------+//| 420 | | + | | | | | | | | |///////////////|60 | | + | | | | | | | | |///////////////| | | + +----|--|-------------+ | | +--|--|---------------+ | | + | | | | 20|10| 70 | | + | | | | | | | | + | | | |500 | | | | + | | | | | | | | + | | | | | | | | + | | | | | | | | + | | | | | | |10| + +--|-------------------------------+ | | +------------------------------|--+ + | | | 490 | + +---------------------------------+ +---------------------------------+ + 500 500 + */ +} + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegionWithRotatedOffAxisSurface) +{ + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr child = LayerChromium::create(); + RefPtr layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(layer); + + // Now rotate the child a little more so it is not axis-aligned. The parent will no longer be occluded by |layer|, but + // the child's occlusion should be unchanged. + + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(95); + childTransform.translate(-250, -250); + + TransformationMatrix layerTransform; + layerTransform.translate(10, 10); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); + setLayerPropertiesForTesting(layer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), true); + + child->setMasksToBounds(true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + IntRect clippedLayerInChild = layerTransform.mapRect(layer->visibleLayerRect()); + + occlusion.enterTargetRenderSurface(child->renderSurface()); + occlusion.markOccludedBehindLayer(layer.get()); + + EXPECT_EQ_RECT(IntRect(), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(clippedLayerInChild, occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child.get(), clippedLayerInChild)); + EXPECT_EQ(true, occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(-1, 0); + EXPECT_EQ(false, occlusion.occluded(child.get(), clippedLayerInChild)); + EXPECT_EQ(false, occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(1, 0); + clippedLayerInChild.move(1, 0); + EXPECT_EQ(false, occlusion.occluded(child.get(), clippedLayerInChild)); + EXPECT_EQ(false, occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(-1, 0); + clippedLayerInChild.move(0, -1); + EXPECT_EQ(false, occlusion.occluded(child.get(), clippedLayerInChild)); + EXPECT_EQ(false, occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(0, 1); + clippedLayerInChild.move(0, 1); + EXPECT_EQ(false, occlusion.occluded(child.get(), clippedLayerInChild)); + EXPECT_EQ(false, occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(0, -1); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(0, 0, 500, 500), occlusion.surfaceUnoccludedContentRect(child.get(), IntRect(0, 0, 500, 500))); + + occlusion.markOccludedBehindLayer(child.get()); + occlusion.finishedTargetRenderSurface(child.get(), child->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + + EXPECT_EQ_RECT(IntRect(), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(75, 55, 1, 1))); + EXPECT_EQ_RECT(IntRect(75, 55, 1, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(75, 55, 1, 1))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(0, 0, 100, 100), occlusion.surfaceUnoccludedContentRect(parent.get(), IntRect(0, 0, 100, 100))); +} + +TEST(CCOcclusionTrackerTest, layerAddedToOccludedRegionWithMultipleOpaqueLayers) +{ + // This is similar to the previous test but now we make a few opaque layers inside of |child| so that the occluded parts of child are not a simple rect. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr child = LayerChromium::create(); + RefPtr layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr layer2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(layer1); + child->addChild(layer2); + + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); + setLayerPropertiesForTesting(layer1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 440), true); + setLayerPropertiesForTesting(layer2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(10, 450), IntSize(500, 60), true); + + child->setMasksToBounds(true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(child->renderSurface()); + occlusion.markOccludedBehindLayer(layer2.get()); + occlusion.markOccludedBehindLayer(layer1.get()); + + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(10, 430, 60, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(11, 430, 60, 70))); + EXPECT_EQ(false, occlusion.occluded(child.get(), IntRect(10, 431, 60, 70))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(10, 430, 60, 70)).isEmpty()); + EXPECT_EQ_RECT(IntRect(9, 430, 1, 70), occlusion.unoccludedContentRect(child.get(), IntRect(9, 430, 60, 70))); + EXPECT_EQ_RECT(IntRect(10, 429, 60, 1), occlusion.unoccludedContentRect(child.get(), IntRect(10, 429, 60, 70))); + EXPECT_EQ_RECT(IntRect(70, 430, 1, 70), occlusion.unoccludedContentRect(child.get(), IntRect(11, 430, 60, 70))); + EXPECT_EQ_RECT(IntRect(10, 500, 60, 1), occlusion.unoccludedContentRect(child.get(), IntRect(10, 431, 60, 70))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(10, 430, 60, 70), occlusion.surfaceUnoccludedContentRect(child.get(), IntRect(10, 430, 60, 70))); + + occlusion.markOccludedBehindLayer(child.get()); + occlusion.finishedTargetRenderSurface(child.get(), child->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 39, 70, 60))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(30, 40, 70, 60)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 40, 1, 60), occlusion.unoccludedContentRect(parent.get(), IntRect(29, 40, 70, 60))); + EXPECT_EQ_RECT(IntRect(30, 39, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 39, 70, 60))); + EXPECT_EQ_RECT(IntRect(100, 40, 1, 60), occlusion.unoccludedContentRect(parent.get(), IntRect(31, 40, 70, 60))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(30, 41, 70, 60))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), occlusion.surfaceUnoccludedContentRect(parent.get(), IntRect(30, 40, 70, 60))); + + + /* Justification for the above occlusion from |layer1| and |layer2|: + + +---------------------+ + | |30 Visible region of |layer1|: ///// + | | Visible region of |layer2|: \\\\\ + | +---------------------------------+ + | | |10 | + | +---------------+-----------------+ | + | | |\\\\\\\\\\\\|//| 420 | | + | | |\\\\\\\\\\\\|//|60 | | + | | |\\\\\\\\\\\\|//| | | + +--|--|------------|--+ | | + 20|10| 70 | | | + | | | | | + | | | | | + | | | | | + | | | | | + | | | | | + | | | |10| + | +------------|-----------------|--+ + | | 490 | + +---------------+-----------------+ + 60 440 + */ +} + +TEST(CCOcclusionTrackerTest, surfaceOcclusionWithOverlappingSiblingSurfaces) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr child1 = LayerChromium::create(); + RefPtr child2 = LayerChromium::create(); + RefPtr layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr layer2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child1); + parent->addChild(child2); + child1->addChild(layer1); + child2->addChild(layer2); + + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child1.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); + setLayerPropertiesForTesting(layer1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), true); + setLayerPropertiesForTesting(child2.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(20, 40), IntSize(500, 500), false); + setLayerPropertiesForTesting(layer2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), true); + + child1->setMasksToBounds(true); + child2->setMasksToBounds(true); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(child2->renderSurface()); + occlusion.markOccludedBehindLayer(layer2.get()); + + EXPECT_EQ_RECT(IntRect(20, 40, 80, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 420, 60, 80), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child2.get(), IntRect(0, 420, 60, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-1, 420, 60, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(0, 419, 60, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(0, 420, 61, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(0, 420, 60, 81))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(0, 420, 60, 80), occlusion.surfaceUnoccludedContentRect(child2.get(), IntRect(0, 420, 60, 80))); + + occlusion.markOccludedBehindLayer(child2.get()); + occlusion.finishedTargetRenderSurface(child2.get(), child2->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + occlusion.enterTargetRenderSurface(child1->renderSurface()); + occlusion.markOccludedBehindLayer(layer1.get()); + + EXPECT_EQ_RECT(IntRect(20, 30, 80, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 430, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child2.get(), IntRect(0, 430, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-1, 430, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(0, 429, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(0, 430, 71, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(0, 430, 70, 71))); + + // Surface is not occluded by things that draw into itself, but the |child1| surface should be occluded by the |child2| surface. + EXPECT_EQ_RECT(IntRect(0, 430, 10, 70), occlusion.surfaceUnoccludedContentRect(child1.get(), IntRect(0, 430, 70, 70))); + + occlusion.markOccludedBehindLayer(child1.get()); + occlusion.finishedTargetRenderSurface(child1.get(), child1->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + + EXPECT_EQ_RECT(IntRect(20, 30, 80, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(20, 30, 80, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(20, 30, 80, 70))); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(20, 40, 80, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(19, 40, 80, 60))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(20, 39, 80, 60))); + + // |child1| and |child2| both draw into parent so they should not occlude it. + EXPECT_EQ_RECT(IntRect(20, 30, 80, 70), occlusion.surfaceUnoccludedContentRect(parent.get(), IntRect(20, 30, 80, 70))); + + + /* Justification for the above occlusion: + 100 + +---------------------+ + | | + | 30 | child1 + | 30+ ---------------------------------+ + 100 | 40| | child2 | + |20+----------------------------------+ | + | | | | | | + | | | | | | + | | | | | | + +--|-|----------------+ | | + | | | | 500 + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | +--------------------------------|-+ + | | + +----------------------------------+ + 500 + */ +} + +TEST(CCOcclusionTrackerTest, surfaceOcclusionInScreenSpace) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr child1 = LayerChromium::create(); + RefPtr child2 = LayerChromium::create(); + RefPtr layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr layer2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child1); + parent->addChild(child2); + child1->addChild(layer1); + child2->addChild(layer2); + + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + // The owning layers have very different bounds from the surfaces that they own. + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child1.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(10, 10), false); + setLayerPropertiesForTesting(layer1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(-10, -10), IntSize(510, 510), true); + setLayerPropertiesForTesting(child2.get(), childTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(20, 40), IntSize(10, 10), false); + setLayerPropertiesForTesting(layer2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(-10, -10), IntSize(510, 510), true); + + // Make them both render surfaces + FilterOperations filters; + filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); + child1->setFilters(filters); + child2->setFilters(filters); + + child1->setMasksToBounds(false); + child2->setMasksToBounds(false); + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + occlusion.enterTargetRenderSurface(child2->renderSurface()); + occlusion.markOccludedBehindLayer(layer2.get()); + + EXPECT_EQ_RECT(IntRect(20, 30, 80, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(-10, 420, 70, 80), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-11, 420, 70, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-10, 419, 70, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-10, 420, 71, 80))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 81))); + + // Surface is not occluded by things that draw into itself. + EXPECT_EQ_RECT(IntRect(-10, 420, 70, 80), occlusion.surfaceUnoccludedContentRect(child2.get(), IntRect(-10, 420, 70, 80))); + EXPECT_EQ(false, occlusion.surfaceOccluded(child2.get(), IntRect(30, 250, 1, 1))); + + occlusion.markOccludedBehindLayer(child2.get()); + occlusion.finishedTargetRenderSurface(child2.get(), child2->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + occlusion.enterTargetRenderSurface(child1->renderSurface()); + occlusion.markOccludedBehindLayer(layer1.get()); + + EXPECT_EQ_RECT(IntRect(20, 20, 80, 80), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(-10, 430, 80, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(true, occlusion.occluded(child2.get(), IntRect(-10, 430, 80, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-11, 430, 80, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-10, 429, 80, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-10, 430, 81, 70))); + EXPECT_EQ(false, occlusion.occluded(child2.get(), IntRect(-10, 430, 80, 71))); + + // Surface is not occluded by things that draw into itself, but the |child1| surface should be occluded by the |child2| surface. + EXPECT_EQ_RECT(IntRect(-10, 430, 10, 80), occlusion.surfaceUnoccludedContentRect(child1.get(), IntRect(-10, 430, 70, 80))); + EXPECT_EQ(true, occlusion.surfaceOccluded(child1.get(), IntRect(0, 430, 70, 80))); + EXPECT_EQ(false, occlusion.surfaceOccluded(child1.get(), IntRect(-1, 430, 70, 80))); + EXPECT_EQ(false, occlusion.surfaceOccluded(child1.get(), IntRect(0, 429, 70, 80))); + EXPECT_EQ(false, occlusion.surfaceOccluded(child1.get(), IntRect(1, 430, 70, 80))); + EXPECT_EQ(false, occlusion.surfaceOccluded(child1.get(), IntRect(0, 431, 70, 80))); + + occlusion.markOccludedBehindLayer(child1.get()); + occlusion.finishedTargetRenderSurface(child1.get(), child1->renderSurface()); + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + + EXPECT_EQ_RECT(IntRect(20, 20, 80, 80), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(20, 20, 80, 80), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(20, 20, 80, 80))); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(30, 20, 70, 80))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(29, 20, 70, 80))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(30, 19, 70, 80))); + + EXPECT_EQ(true, occlusion.occluded(parent.get(), IntRect(20, 30, 80, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(19, 30, 80, 70))); + EXPECT_EQ(false, occlusion.occluded(parent.get(), IntRect(20, 29, 80, 70))); + + // |child1| and |child2| both draw into parent so they should not occlude it. + EXPECT_EQ_RECT(IntRect(20, 20, 80, 80), occlusion.surfaceUnoccludedContentRect(parent.get(), IntRect(20, 20, 80, 80))); + EXPECT_EQ(false, occlusion.surfaceOccluded(parent.get(), IntRect(50, 50, 1, 1))); + + + /* Justification for the above occlusion: + 100 + +---------------------+ + | 20 | layer1 + | 30+ ---------------------------------+ + 100 | 30| | layer2 | + |20+----------------------------------+ | + | | | | | | + | | | | | | + | | | | | | + +--|-|----------------+ | | + | | | | 500 + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | +--------------------------------|-+ + | | + +----------------------------------+ + 500 + */ +} + +TEST(CCOcclusionTrackerTest, occlusionInteractionWithFilters) +{ + // This tests that the right transforms are being used. + TestCCOcclusionTracker occlusion; + const TransformationMatrix identityMatrix; + RefPtr parent = LayerChromium::create(); + RefPtr blurLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr opacityLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr opaqueLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(blurLayer); + parent->addChild(opacityLayer); + parent->addChild(opaqueLayer); + + TransformationMatrix layerTransform; + layerTransform.translate(250, 250); + layerTransform.rotate(90); + layerTransform.translate(-250, -250); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(blurLayer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setLayerPropertiesForTesting(opaqueLayer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setLayerPropertiesForTesting(opacityLayer.get(), layerTransform, identityMatrix, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + + { + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(10, WebCore::Percent), FilterOperation::BLUR)); + blurLayer->setFilters(filters); + } + + { + FilterOperations filters; + filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); + opaqueLayer->setFilters(filters); + } + + { + FilterOperations filters; + filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::OPACITY)); + opacityLayer->setFilters(filters); + } + + + Vector > renderSurfaceLayerList; + Vector > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + // Opacity layer won't contribute to occlusion. + occlusion.enterTargetRenderSurface(opacityLayer->renderSurface()); + occlusion.markOccludedBehindLayer(opacityLayer.get()); + occlusion.finishedTargetRenderSurface(opacityLayer.get(), opacityLayer->renderSurface()); + + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // And has nothing to contribute to its parent surface. + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // Opaque layer will contribute to occlusion. + occlusion.enterTargetRenderSurface(opaqueLayer->renderSurface()); + occlusion.markOccludedBehindLayer(opaqueLayer.get()); + occlusion.finishedTargetRenderSurface(opaqueLayer.get(), opaqueLayer->renderSurface()); + + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 430, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + // And it gets translated to the parent surface. + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + // The blur layer needs to throw away any occlusion from outside its subtree. + occlusion.enterTargetRenderSurface(blurLayer->renderSurface()); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // And it won't contribute to occlusion. + occlusion.markOccludedBehindLayer(blurLayer.get()); + occlusion.finishedTargetRenderSurface(blurLayer.get(), blurLayer->renderSurface()); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // But the opaque layer's occlusion is preserved on the parent. + occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); +} + +} // namespace