diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-05-07 11:21:11 +0200 |
commit | 2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47 (patch) | |
tree | 988e8c5b116dd0466244ae2fe5af8ee9be926d76 /Source/WebKit/chromium/tests | |
parent | dd91e772430dc294e3bf478c119ef8d43c0a3358 (diff) | |
download | qtwebkit-2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47.tar.gz |
Imported WebKit commit 7e538425aa020340619e927792f3d895061fb54b (http://svn.webkit.org/repository/webkit/trunk@116286)
Diffstat (limited to 'Source/WebKit/chromium/tests')
66 files changed, 10437 insertions, 4393 deletions
diff --git a/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp b/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp index a168ebca9..bdf3a3ab4 100644 --- a/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp +++ b/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp @@ -432,9 +432,40 @@ TEST_F(AssociatedURLLoaderTest, RedirectSuccess) EXPECT_TRUE(m_didFinishLoading); } -// Test a successful redirect and cross-origin load using CORS. -// FIXME: Enable this when DocumentThreadableLoader supports cross-origin redirects. -TEST_F(AssociatedURLLoaderTest, DISABLED_RedirectCrossOriginWithAccessControlSuccess) +// Test that a cross origin redirect response without CORS headers fails. +TEST_F(AssociatedURLLoaderTest, RedirectCrossOriginWithAccessControlFailure) +{ + GURL url = GURL("http://www.test.com/RedirectCrossOriginWithAccessControlFailure.html"); + char redirect[] = "http://www.other.com/RedirectCrossOriginWithAccessControlFailure.html"; // Cross-origin + GURL redirectURL = GURL(redirect); + + WebURLRequest request; + request.initialize(); + request.setURL(url); + + // Create a redirect response without CORS headers. + m_expectedRedirectResponse = WebURLResponse(); + m_expectedRedirectResponse.initialize(); + m_expectedRedirectResponse.setMIMEType("text/html"); + m_expectedRedirectResponse.setHTTPStatusCode(301); + m_expectedRedirectResponse.setHTTPHeaderField("Location", redirect); + webkit_support::RegisterMockedURL(url, m_expectedRedirectResponse, m_frameFilePath); + + WebURLLoaderOptions options; + options.crossOriginRequestPolicy = WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; + m_expectedLoader = createAssociatedURLLoader(options); + EXPECT_TRUE(m_expectedLoader); + m_expectedLoader->loadAsynchronously(request, this); + serveRequests(); + // We should not receive a notification for the redirect or any response. + EXPECT_FALSE(m_willSendRequest); + EXPECT_FALSE(m_didReceiveResponse); + EXPECT_FALSE(m_didReceiveData); + EXPECT_FALSE(m_didFail); +} + +// Test that a cross origin redirect response with CORS headers that allow the requesting origin succeeds. +TEST_F(AssociatedURLLoaderTest, RedirectCrossOriginWithAccessControlSuccess) { GURL url = GURL("http://www.test.com/RedirectCrossOriginWithAccessControlSuccess.html"); char redirect[] = "http://www.other.com/RedirectCrossOriginWithAccessControlSuccess.html"; // Cross-origin @@ -444,11 +475,13 @@ TEST_F(AssociatedURLLoaderTest, DISABLED_RedirectCrossOriginWithAccessControlSuc request.initialize(); request.setURL(url); + // Create a redirect response that allows the redirect to pass the access control checks. m_expectedRedirectResponse = WebURLResponse(); m_expectedRedirectResponse.initialize(); m_expectedRedirectResponse.setMIMEType("text/html"); m_expectedRedirectResponse.setHTTPStatusCode(301); m_expectedRedirectResponse.setHTTPHeaderField("Location", redirect); + m_expectedRedirectResponse.addHTTPHeaderField("access-control-allow-origin", "*"); webkit_support::RegisterMockedURL(url, m_expectedRedirectResponse, m_frameFilePath); m_expectedNewRequest = WebURLRequest(); @@ -467,7 +500,8 @@ TEST_F(AssociatedURLLoaderTest, DISABLED_RedirectCrossOriginWithAccessControlSuc EXPECT_TRUE(m_expectedLoader); m_expectedLoader->loadAsynchronously(request, this); serveRequests(); - EXPECT_TRUE(m_willSendRequest); + // We should not receive a notification for the redirect. + EXPECT_FALSE(m_willSendRequest); EXPECT_TRUE(m_didReceiveResponse); EXPECT_TRUE(m_didReceiveData); EXPECT_TRUE(m_didFinishLoading); diff --git a/Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp b/Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp index e14ef9bc0..a1ab770b7 100644 --- a/Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp +++ b/Source/WebKit/chromium/tests/CCActiveAnimationTest.cpp @@ -80,6 +80,17 @@ TEST(CCActiveAnimationTest, TrimTimeStartTime) EXPECT_EQ(1, anim->trimTimeToCurrentIteration(6)); } +TEST(CCActiveAnimationTest, TrimTimeTimeOffset) +{ + OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1)); + anim->setTimeOffset(4); + anim->setStartTime(4); + EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0)); + EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5)); + EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1)); + EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1)); +} + TEST(CCActiveAnimationTest, TrimTimePauseResume) { OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1)); @@ -93,6 +104,19 @@ TEST(CCActiveAnimationTest, TrimTimePauseResume) EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1024.5)); } +TEST(CCActiveAnimationTest, TrimTimeSuspendResume) +{ + OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1)); + anim->setRunState(CCActiveAnimation::Running, 0); + EXPECT_EQ(0, anim->trimTimeToCurrentIteration(0)); + EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(0.5)); + anim->suspend(0.5); + EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024)); + anim->resume(1024); + EXPECT_EQ(0.5, anim->trimTimeToCurrentIteration(1024)); + EXPECT_EQ(1, anim->trimTimeToCurrentIteration(1024.5)); +} + TEST(CCActiveAnimationTest, IsFinishedAtZeroIterations) { OwnPtr<CCActiveAnimation> anim(createActiveAnimation(0)); @@ -160,4 +184,35 @@ TEST(CCActiveAnimationTest, IsFinished) EXPECT_TRUE(anim->isFinished()); } +TEST(CCActiveAnimationTest, IsFinishedNeedsSynchronizedStartTime) +{ + OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1)); + anim->setRunState(CCActiveAnimation::Running, 2); + EXPECT_FALSE(anim->isFinished()); + anim->setRunState(CCActiveAnimation::Paused, 2); + EXPECT_FALSE(anim->isFinished()); + anim->setRunState(CCActiveAnimation::WaitingForNextTick, 2); + EXPECT_FALSE(anim->isFinished()); + anim->setRunState(CCActiveAnimation::WaitingForTargetAvailability, 2); + EXPECT_FALSE(anim->isFinished()); + anim->setRunState(CCActiveAnimation::WaitingForStartTime, 2); + EXPECT_FALSE(anim->isFinished()); + anim->setRunState(CCActiveAnimation::Finished, 0); + EXPECT_TRUE(anim->isFinished()); + anim->setRunState(CCActiveAnimation::Aborted, 0); + EXPECT_TRUE(anim->isFinished()); +} + +TEST(CCActiveAnimationTest, RunStateChangesIgnoredWhileSuspended) +{ + OwnPtr<CCActiveAnimation> anim(createActiveAnimation(1)); + anim->suspend(0); + EXPECT_EQ(CCActiveAnimation::Paused, anim->runState()); + anim->setRunState(CCActiveAnimation::Running, 0); + EXPECT_EQ(CCActiveAnimation::Paused, anim->runState()); + anim->resume(0); + anim->setRunState(CCActiveAnimation::Running, 0); + EXPECT_EQ(CCActiveAnimation::Running, anim->runState()); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp b/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp index c9240e31f..36f79818e 100644 --- a/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp +++ b/Source/WebKit/chromium/tests/CCAnimationTestCommon.cpp @@ -28,14 +28,16 @@ #include "GraphicsLayer.h" #include "LayerChromium.h" +#include "TranslateTransformOperation.h" #include "cc/CCLayerAnimationController.h" +#include "cc/CCLayerImpl.h" using namespace WebCore; namespace { template <class Target> -void addOpacityTransition(Target& target, double duration, float startOpacity, float endOpacity) +void addOpacityTransition(Target& target, double duration, float startOpacity, float endOpacity, bool useTimingFunction) { WebCore::KeyframeValueList values(AnimatedPropertyOpacity); if (duration > 0) @@ -45,11 +47,32 @@ void addOpacityTransition(Target& target, double duration, float startOpacity, f RefPtr<Animation> animation = Animation::create(); animation->setDuration(duration); + if (useTimingFunction) + animation->setTimingFunction(LinearTimingFunction::create()); + IntSize boxSize; target.addAnimation(values, boxSize, animation.get(), 0, 0, 0); } +template <class Target> +void addAnimatedTransform(Target& target, double duration, int deltaX, int deltaY) +{ + static int id = 0; + WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); + + TransformOperations operations; + operations.operations().append(TranslateTransformOperation::create(Length(deltaX, WebCore::Fixed), Length(deltaY, WebCore::Fixed), TransformOperation::TRANSLATE_X)); + values.insert(new TransformAnimationValue(0, &operations)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + + IntSize boxSize; + + target.addAnimation(values, boxSize, animation.get(), ++id, 0, 0); +} + } // namespace namespace WebKitTests { @@ -106,12 +129,12 @@ float FakeFloatTransition::getValue(double time) const return (1 - time) * m_from + time * m_to; } -FakeLayerAnimationControllerImplClient::FakeLayerAnimationControllerImplClient() +FakeLayerAnimationControllerClient::FakeLayerAnimationControllerClient() : m_opacity(0) { } -FakeLayerAnimationControllerImplClient::~FakeLayerAnimationControllerImplClient() +FakeLayerAnimationControllerClient::~FakeLayerAnimationControllerClient() { } @@ -120,14 +143,34 @@ PassOwnPtr<WebCore::CCAnimationCurve> FakeFloatTransition::clone() const return adoptPtr(new FakeFloatTransition(*this)); } -void addOpacityTransitionToController(WebCore::CCLayerAnimationController& controller, double duration, float startOpacity, float endOpacity) +void addOpacityTransitionToController(WebCore::CCLayerAnimationController& controller, double duration, float startOpacity, float endOpacity, bool useTimingFunction) +{ + addOpacityTransition(controller, duration, startOpacity, endOpacity, useTimingFunction); +} + +void addAnimatedTransformToController(WebCore::CCLayerAnimationController& controller, double duration, int deltaX, int deltaY) +{ + addAnimatedTransform(controller, duration, deltaX, deltaY); +} + +void addOpacityTransitionToLayer(WebCore::LayerChromium& layer, double duration, float startOpacity, float endOpacity, bool useTimingFunction) +{ + addOpacityTransition(layer, duration, startOpacity, endOpacity, useTimingFunction); +} + +void addOpacityTransitionToLayer(WebCore::CCLayerImpl& layer, double duration, float startOpacity, float endOpacity, bool useTimingFunction) +{ + addOpacityTransition(*layer.layerAnimationController(), duration, startOpacity, endOpacity, useTimingFunction); +} + +void addAnimatedTransformToLayer(WebCore::LayerChromium& layer, double duration, int deltaX, int deltaY) { - addOpacityTransition(controller, duration, startOpacity, endOpacity); + addAnimatedTransform(layer, duration, deltaX, deltaY); } -void addOpacityTransitionToLayer(WebCore::LayerChromium& layer, double duration, float startOpacity, float endOpacity) +void addAnimatedTransformToLayer(WebCore::CCLayerImpl& layer, double duration, int deltaX, int deltaY) { - addOpacityTransition(layer, duration, startOpacity, endOpacity); + addAnimatedTransform(*layer.layerAnimationController(), duration, deltaX, deltaY); } } // namespace WebKitTests diff --git a/Source/WebKit/chromium/tests/CCAnimationTestCommon.h b/Source/WebKit/chromium/tests/CCAnimationTestCommon.h index d13e74ca1..776b75c8d 100644 --- a/Source/WebKit/chromium/tests/CCAnimationTestCommon.h +++ b/Source/WebKit/chromium/tests/CCAnimationTestCommon.h @@ -26,12 +26,12 @@ #define CCAnimationTestCommon_h #include "cc/CCAnimationCurve.h" -#include "cc/CCLayerAnimationControllerImpl.h" +#include "cc/CCLayerAnimationController.h" #include <wtf/OwnPtr.h> namespace WebCore { -class CCLayerAnimationController; +class CCLayerImpl; class LayerChromium; } @@ -42,9 +42,9 @@ public: FakeFloatAnimationCurve(); virtual ~FakeFloatAnimationCurve(); - virtual double duration() const { return 1; } - virtual float getValue(double now) const { return 0; } - virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const; + virtual double duration() const OVERRIDE { return 1; } + virtual float getValue(double now) const OVERRIDE { return 0; } + virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE; }; class FakeTransformTransition : public WebCore::CCTransformAnimationCurve { @@ -52,10 +52,10 @@ public: FakeTransformTransition(double duration); virtual ~FakeTransformTransition(); - virtual double duration() const { return m_duration; } - virtual WebCore::TransformationMatrix getValue(double time, const WebCore::IntSize&) const; + virtual double duration() const OVERRIDE { return m_duration; } + virtual WebCore::TransformationMatrix getValue(double time, const WebCore::IntSize&) const OVERRIDE; - virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const; + virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE; private: double m_duration; @@ -66,10 +66,10 @@ public: FakeFloatTransition(double duration, float from, float to); virtual ~FakeFloatTransition(); - virtual double duration() const { return m_duration; } - virtual float getValue(double time) const; + virtual double duration() const OVERRIDE { return m_duration; } + virtual float getValue(double time) const OVERRIDE; - virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const; + virtual PassOwnPtr<WebCore::CCAnimationCurve> clone() const OVERRIDE; private: double m_duration; @@ -77,17 +77,18 @@ private: float m_to; }; -class FakeLayerAnimationControllerImplClient : public WebCore::CCLayerAnimationControllerImplClient { +class FakeLayerAnimationControllerClient : public WebCore::CCLayerAnimationControllerClient { public: - FakeLayerAnimationControllerImplClient(); - virtual ~FakeLayerAnimationControllerImplClient(); + FakeLayerAnimationControllerClient(); + virtual ~FakeLayerAnimationControllerClient(); - virtual int id() const { return 0; } - virtual float opacity() const { return m_opacity; } - virtual void setOpacity(float opacity) { m_opacity = opacity; } - virtual const WebCore::TransformationMatrix& transform() const { return m_transform; } - virtual void setTransform(const WebCore::TransformationMatrix& transform) { m_transform = transform; } - virtual const WebCore::IntSize& bounds() const { return m_bounds; } + // CCLayerAnimationControllerClient implementation + virtual int id() const OVERRIDE { return 0; } + virtual void setOpacityFromAnimation(float opacity) OVERRIDE { m_opacity = opacity; } + virtual float opacity() const OVERRIDE { return m_opacity; } + virtual void setTransformFromAnimation(const WebCore::TransformationMatrix& transform) OVERRIDE { m_transform = transform; } + virtual const WebCore::TransformationMatrix& transform() const OVERRIDE { return m_transform; } + virtual const WebCore::IntSize& bounds() const OVERRIDE { return m_bounds; } private: float m_opacity; @@ -95,9 +96,14 @@ private: WebCore::IntSize m_bounds; }; -void addOpacityTransitionToController(WebCore::CCLayerAnimationController&, double duration, float startOpacity, float endOpacity); +void addOpacityTransitionToController(WebCore::CCLayerAnimationController&, double duration, float startOpacity, float endOpacity, bool useTimingFunction); +void addAnimatedTransformToController(WebCore::CCLayerAnimationController&, double duration, int deltaX, int deltaY); -void addOpacityTransitionToLayer(WebCore::LayerChromium&, double duration, float startOpacity, float endOpacity); +void addOpacityTransitionToLayer(WebCore::LayerChromium&, double duration, float startOpacity, float endOpacity, bool useTimingFunction); +void addOpacityTransitionToLayer(WebCore::CCLayerImpl&, double duration, float startOpacity, float endOpacity, bool useTimingFunction); + +void addAnimatedTransformToLayer(WebCore::LayerChromium&, double duration, int deltaX, int deltaY); +void addAnimatedTransformToLayer(WebCore::CCLayerImpl&, double duration, int deltaX, int deltaY); } // namespace WebKitTests diff --git a/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp b/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp index 74d635b40..11a88784b 100644 --- a/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp +++ b/Source/WebKit/chromium/tests/CCDamageTrackerTest.cpp @@ -30,6 +30,7 @@ #include "cc/CCLayerImpl.h" #include "cc/CCLayerSorter.h" #include "cc/CCLayerTreeHostCommon.h" +#include "cc/CCMathUtil.h" #include "cc/CCSingleThreadProxy.h" #include <gtest/gtest.h> @@ -70,7 +71,7 @@ void emulateDrawingOneFrame(CCLayerImpl* root) // Iterate back-to-front, so that damage correctly propagates from descendant surfaces to ancestors. for (int i = renderSurfaceLayerList.size() - 1; i >= 0; --i) { CCRenderSurface* targetSurface = renderSurfaceLayerList[i]->renderSurface(); - targetSurface->damageTracker()->updateDamageTrackingState(targetSurface->layerList(), targetSurface->owningLayerId(), renderSurfaceLayerList[i]->maskLayer()); + targetSurface->damageTracker()->updateDamageTrackingState(targetSurface->layerList(), targetSurface->owningLayerId(), targetSurface->surfacePropertyChangedOnlyFromDescendant(), targetSurface->contentRect(), renderSurfaceLayerList[i]->maskLayer(), renderSurfaceLayerList[i]->filters()); } root->resetAllChangeTrackingForSubtree(); @@ -180,7 +181,7 @@ TEST_F(CCDamageTrackerTest, sanityCheckTestTreeWithOneSurface) OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); - EXPECT_EQ(static_cast<size_t>(2), root->renderSurface()->layerList().size()); + EXPECT_EQ(2u, root->renderSurface()->layerList().size()); EXPECT_EQ(1, root->renderSurface()->layerList()[0]->id()); EXPECT_EQ(2, root->renderSurface()->layerList()[1]->id()); @@ -202,8 +203,8 @@ TEST_F(CCDamageTrackerTest, sanityCheckTestTreeWithTwoSurfaces) ASSERT_TRUE(child1->renderSurface()); EXPECT_FALSE(child2->renderSurface()); - EXPECT_EQ(static_cast<size_t>(3), root->renderSurface()->layerList().size()); - EXPECT_EQ(static_cast<size_t>(2), child1->renderSurface()->layerList().size()); + EXPECT_EQ(3u, root->renderSurface()->layerList().size()); + EXPECT_EQ(2u, child1->renderSurface()->layerList().size()); // The render surface for child1 only has a contentRect that encloses grandChild1 and grandChild2, because child1 does not draw content. EXPECT_FLOAT_RECT_EQ(FloatRect(190, 190, 16, 18), childDamageRect); @@ -254,7 +255,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForPropertyChanges) // Sanity check - we should not have accidentally created a separate render surface for the translucent layer. ASSERT_FALSE(child->renderSurface()); - ASSERT_EQ(static_cast<size_t>(2), root->renderSurface()->layerList().size()); + ASSERT_EQ(2u, root->renderSurface()->layerList().size()); // Damage should be the entire child layer in targetSurface space. FloatRect expectedRect = FloatRect(100, 100, 30, 30); @@ -315,6 +316,146 @@ TEST_F(CCDamageTrackerTest, verifyDamageForTransformedLayer) EXPECT_FLOAT_RECT_EQ(expectedRect, rootDamageRect); } +TEST_F(CCDamageTrackerTest, verifyDamageForPerspectiveClippedLayer) +{ + // If a layer has a perspective transform that causes w < 0, then not clipping the + // layer can cause an invalid damage rect. This test checks that the w < 0 case is + // tracked properly. + // + // The transform is constructed so that if w < 0 clipping is not performed, the + // incorrect rect will be very small, specifically: position (-3.153448, -2.750628) and size 8.548689 x 5.661383. + // Instead, the correctly transformed rect should actually be very huge (i.e. in theory, infinite) + + OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); + CCLayerImpl* child = root->children()[0].get(); + + TransformationMatrix transform; + transform.applyPerspective(1); + transform.translate3d(-150, -50, 0); + transform.rotate3d(0, 45, 0); + transform.translate3d(-50, -50, 0); + + // Set up the child + child->setPosition(FloatPoint(0, 0)); + child->setBounds(IntSize(100, 100)); + child->setTransform(transform); + emulateDrawingOneFrame(root.get()); + + // Sanity check that the child layer's bounds would actually get clipped by w < 0, + // otherwise this test is not actually testing the intended scenario. + FloatQuad testQuad(FloatRect(FloatPoint::zero(), FloatSize(100, 100))); + bool clipped = false; + CCMathUtil::mapQuad(transform, testQuad, clipped); + EXPECT_TRUE(clipped); + + // Damage the child without moving it. + child->setOpacity(0.5); + emulateDrawingOneFrame(root.get()); + + // The expected damage should cover the entire root surface (500x500), but we don't + // care whether the damage rect was clamped or is larger than the surface for this test. + FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_GE(rootDamageRect.width(), 500); + EXPECT_GE(rootDamageRect.height(), 500); +} + +TEST_F(CCDamageTrackerTest, verifyDamageForBlurredSurface) +{ + OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); + CCLayerImpl* child = root->children()[0].get(); + + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(5, WebCore::Fixed), FilterOperation::BLUR)); + int outsetTop, outsetRight, outsetBottom, outsetLeft; + filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); + root->setFilters(filters); + + // Setting the filter will damage the whole surface. + emulateDrawingOneFrame(root.get()); + + // Setting the update rect should cause the corresponding damage to the surface, blurred based on the size of the blur filter. + child->setUpdateRect(FloatRect(10, 11, 12, 13)); + emulateDrawingOneFrame(root.get()); + + // Damage position on the surface should be: position of updateRect (10, 11) relative to the child (100, 100), but expanded by the blur outsets. + FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + FloatRect expectedDamageRect = FloatRect(110, 111, 12, 13); + expectedDamageRect.move(-outsetLeft, -outsetTop); + expectedDamageRect.expand(outsetLeft + outsetRight, outsetTop + outsetBottom); + EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect); +} + +TEST_F(CCDamageTrackerTest, verifyDamageForBackgroundBlurredChild) +{ + OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces(); + CCLayerImpl* child1 = root->children()[0].get(); + CCLayerImpl* child2 = root->children()[1].get(); + + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(2, WebCore::Fixed), FilterOperation::BLUR)); + int outsetTop, outsetRight, outsetBottom, outsetLeft; + filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); + child1->setBackgroundFilters(filters); + + // Setting the filter will damage the whole surface. + emulateDrawingOneFrame(root.get()); + + // CASE 1: Setting the update rect should cause the corresponding damage to + // the surface, blurred based on the size of the child's background blur + // filter. + root->setUpdateRect(FloatRect(297, 297, 2, 2)); + + emulateDrawingOneFrame(root.get()); + + FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + // Damage position on the surface should be a composition of the damage on the root and on child2. + // Damage on the root should be: position of updateRect (297, 297), but expanded by the blur outsets. + FloatRect expectedDamageRect = FloatRect(297, 297, 2, 2); + expectedDamageRect.move(-outsetLeft, -outsetTop); + expectedDamageRect.expand(outsetLeft + outsetRight, outsetTop + outsetBottom); + EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect); + + // CASE 2: Setting the update rect should cause the corresponding damage to + // the surface, blurred based on the size of the child's background blur + // filter. Since the damage extends to the right/bottom outside of the + // blurred layer, only the left/top should end up expanded. + root->setUpdateRect(FloatRect(297, 297, 30, 30)); + + emulateDrawingOneFrame(root.get()); + + rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + // Damage position on the surface should be a composition of the damage on the root and on child2. + // Damage on the root should be: position of updateRect (297, 297), but expanded on the left/top + // by the blur outsets. + expectedDamageRect = FloatRect(297, 297, 30, 30); + expectedDamageRect.move(-outsetLeft, -outsetTop); + expectedDamageRect.expand(outsetLeft, outsetTop); + EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect); + + // CASE 3: Setting this update rect outside the contentBounds of the blurred + // child1 will not cause it to be expanded. + root->setUpdateRect(FloatRect(30, 30, 2, 2)); + + emulateDrawingOneFrame(root.get()); + + rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + // Damage on the root should be: position of updateRect (30, 30), not + // expanded. + expectedDamageRect = FloatRect(30, 30, 2, 2); + EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect); + + // CASE 4: Setting the update rect on child2, which is above child1, will + // not get blurred by child1, so it does not need to get expanded. + child2->setUpdateRect(FloatRect(0, 0, 1, 1)); + + emulateDrawingOneFrame(root.get()); + + rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + // Damage on child2 should be: position of updateRect offset by the child's position (11, 11), and not expanded by anything. + expectedDamageRect = FloatRect(11, 11, 1, 1); + EXPECT_FLOAT_RECT_EQ(expectedDamageRect, rootDamageRect); +} + TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer) { OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); @@ -334,7 +475,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer) emulateDrawingOneFrame(root.get()); // Sanity check - all 3 layers should be on the same render surface; render surfaces are tested elsewhere. - ASSERT_EQ(static_cast<size_t>(3), root->renderSurface()->layerList().size()); + ASSERT_EQ(3u, root->renderSurface()->layerList().size()); FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); EXPECT_FLOAT_RECT_EQ(FloatRect(400, 380, 6, 8), rootDamageRect); @@ -353,6 +494,36 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingLayer) EXPECT_FLOAT_RECT_EQ(FloatRect(100, 100, 30, 30), rootDamageRect); } +TEST_F(CCDamageTrackerTest, verifyDamageForNewUnchangedLayer) +{ + // If child2 is added to the layer tree, but it doesn't have any explicit damage of + // its own, it should still indeed damage the target surface. + + OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); + + { + OwnPtr<CCLayerImpl> child2 = CCLayerImpl::create(3); + child2->setPosition(FloatPoint(400, 380)); + child2->setAnchorPoint(FloatPoint::zero()); + child2->setBounds(IntSize(6, 8)); + child2->setDrawsContent(true); + child2->resetAllChangeTrackingForSubtree(); + // Sanity check the initial conditions of the test, if these asserts trigger, it + // means the test no longer actually covers the intended scenario. + ASSERT_FALSE(child2->layerPropertyChanged()); + ASSERT_TRUE(child2->updateRect().isEmpty()); + root->addChild(child2.release()); + } + + emulateDrawingOneFrame(root.get()); + + // Sanity check - all 3 layers should be on the same render surface; render surfaces are tested elsewhere. + ASSERT_EQ(3u, root->renderSurface()->layerList().size()); + + FloatRect rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_FLOAT_RECT_EQ(FloatRect(400, 380, 6, 8), rootDamageRect); +} + TEST_F(CCDamageTrackerTest, verifyDamageForMultipleLayers) { OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); @@ -402,8 +573,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForNestedSurfaces) // CASE 2: Same as previous case, but with additional damage elsewhere that should be properly unioned. // - child1 surface damage in root surface space: FloatRect(300, 300, 6, 8); // - child2 damage in root surface space: FloatRect(11, 11, 18, 18); - grandChild1->setOpacity(0.7); - child2->setOpacity(0.7); + grandChild1->setOpacity(0.7f); + child2->setOpacity(0.7f); emulateDrawingOneFrame(root.get()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -485,7 +656,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingRenderSurfaces) // Sanity check that there is only one surface now. ASSERT_FALSE(child1->renderSurface()); - ASSERT_EQ(static_cast<size_t>(4), root->renderSurface()->layerList().size()); + ASSERT_EQ(4u, root->renderSurface()->layerList().size()); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); EXPECT_FLOAT_RECT_EQ(FloatRect(290, 290, 16, 18), rootDamageRect); @@ -503,8 +674,8 @@ TEST_F(CCDamageTrackerTest, verifyDamageForAddingAndRemovingRenderSurfaces) // Sanity check that there is a new surface now. ASSERT_TRUE(child1->renderSurface()); - EXPECT_EQ(static_cast<size_t>(3), root->renderSurface()->layerList().size()); - EXPECT_EQ(static_cast<size_t>(2), child1->renderSurface()->layerList().size()); + EXPECT_EQ(3u, root->renderSurface()->layerList().size()); + EXPECT_EQ(2u, child1->renderSurface()->layerList().size()); childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); rootDamageRect = root->renderSurface()->damageTracker()->currentDamageRect(); @@ -746,7 +917,7 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMask) ASSERT_TRUE(grandChild1->renderSurface()); // CASE 1: a property change on the mask should damage only the reflected region on the target surface. - replicaMaskLayer->setOpacity(0.6); + replicaMaskLayer->setOpacity(0.6f); emulateDrawingOneFrame(root.get()); FloatRect grandChildDamageRect = grandChild1->renderSurface()->damageTracker()->currentDamageRect(); @@ -767,7 +938,53 @@ TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMask) EXPECT_FLOAT_RECT_EQ(FloatRect(194, 200, 6, 8), childDamageRect); } -TEST_F(CCDamageTrackerTest, verifyDamageWhenReset) +TEST_F(CCDamageTrackerTest, verifyDamageForReplicaMaskWithAnchor) +{ + OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithTwoSurfaces(); + CCLayerImpl* child1 = root->children()[0].get(); + CCLayerImpl* grandChild1 = child1->children()[0].get(); + + // Verify that the correct replicaOriginTransform is used for the replicaMask; the + // incorrect old code incorrectly accounted for the anchor for the replica. A + // non-zero anchor point should not affect the replica reflection. + + grandChild1->setAnchorPoint(FloatPoint(1.0, 0.0)); // This is the anchor being tested. + + { + OwnPtr<CCLayerImpl> grandChild1Replica = CCLayerImpl::create(6); + grandChild1Replica->setPosition(FloatPoint::zero()); + grandChild1Replica->setAnchorPoint(FloatPoint::zero()); // note, this is not the anchor being tested. + TransformationMatrix reflection; + reflection.scale3d(-1.0, 1.0, 1.0); + grandChild1Replica->setTransform(reflection); + grandChild1->setReplicaLayer(grandChild1Replica.release()); + } + CCLayerImpl* grandChild1Replica = grandChild1->replicaLayer(); + + // Set up the mask layer on the replica layer + { + OwnPtr<CCLayerImpl> replicaMaskLayer = CCLayerImpl::create(7); + replicaMaskLayer->setPosition(FloatPoint::zero()); + replicaMaskLayer->setAnchorPoint(FloatPoint::zero()); // note, this is not the anchor being tested. + replicaMaskLayer->setBounds(grandChild1->bounds()); + grandChild1Replica->setMaskLayer(replicaMaskLayer.release()); + } + CCLayerImpl* replicaMaskLayer = grandChild1Replica->maskLayer(); + + emulateDrawingOneFrame(root.get()); + + // Sanity check that the appropriate render surfaces were created + ASSERT_TRUE(grandChild1->renderSurface()); + + // A property change on the replicaMask should damage the reflected region on the target surface. + replicaMaskLayer->setOpacity(0.6f); + emulateDrawingOneFrame(root.get()); + + FloatRect childDamageRect = child1->renderSurface()->damageTracker()->currentDamageRect(); + EXPECT_FLOAT_RECT_EQ(FloatRect(194, 200, 6, 8), childDamageRect); +} + +TEST_F(CCDamageTrackerTest, verifyDamageWhenForcedFullDamage) { OwnPtr<CCLayerImpl> root = createAndSetUpTestTreeWithOneSurface(); CCLayerImpl* child = root->children()[0].get(); @@ -790,4 +1007,21 @@ TEST_F(CCDamageTrackerTest, verifyDamageWhenReset) EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 500, 500), rootDamageRect); } +TEST_F(CCDamageTrackerTest, verifyDamageForEmptyLayerList) +{ + // Though it should never happen, its a good idea to verify that the damage tracker + // does not crash when it receives an empty layerList. + + OwnPtr<CCLayerImpl> root = CCLayerImpl::create(1); + root->createRenderSurface(); + + ASSERT_TRUE(root->renderSurface() == root->targetRenderSurface()); + CCRenderSurface* targetSurface = root->renderSurface(); + targetSurface->clearLayerList(); + targetSurface->damageTracker()->updateDamageTrackingState(targetSurface->layerList(), targetSurface->owningLayerId(), false, IntRect(), 0, FilterOperations()); + + FloatRect damageRect = targetSurface->damageTracker()->currentDamageRect(); + EXPECT_TRUE(damageRect.isEmpty()); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCDelayBasedTimeSourceTest.cpp b/Source/WebKit/chromium/tests/CCDelayBasedTimeSourceTest.cpp index 867d56ea8..93e84a4af 100644 --- a/Source/WebKit/chromium/tests/CCDelayBasedTimeSourceTest.cpp +++ b/Source/WebKit/chromium/tests/CCDelayBasedTimeSourceTest.cpp @@ -41,15 +41,15 @@ TEST(CCDelayBasedTimeSourceTest, TaskPostedAndTickCalled) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); - timer->setMonotonicallyIncreasingTimeMs(0); + timer->setMonotonicallyIncreasingTime(0); timer->setActive(true); EXPECT_TRUE(timer->active()); EXPECT_TRUE(thread.hasPendingTask()); - timer->setMonotonicallyIncreasingTimeMs(16); + timer->setMonotonicallyIncreasingTime(0.016); thread.runPendingTask(); EXPECT_TRUE(timer->active()); EXPECT_TRUE(client.tickCalled()); @@ -59,7 +59,7 @@ TEST(CCDelayBasedTimeSource, TickNotCalledWithTaskPosted) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); timer->setActive(true); EXPECT_TRUE(thread.hasPendingTask()); @@ -72,7 +72,7 @@ TEST(CCDelayBasedTimeSource, StartTwiceEnqueuesOneTask) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); timer->setActive(true); EXPECT_TRUE(thread.hasPendingTask()); @@ -85,7 +85,7 @@ TEST(CCDelayBasedTimeSource, StartWhenRunningDoesntTick) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); timer->setActive(true); thread.runPendingTask(); @@ -100,19 +100,19 @@ TEST(CCDelayBasedTimeSource, NextDelaySaneWhenExactlyOnRequestedTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - double interval = 1000.0 / 60.0; + double interval = 1.0 / 60.0; RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread); timer->setClient(&client); timer->setActive(true); // Run the first task, as that activates the timer and picks up a timebase. thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); - timer->setMonotonicallyIncreasingTimeMs(interval); + timer->setMonotonicallyIncreasingTime(interval); thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); } // At 60Hz, when the tick returns at slightly after the requested next time, make sure @@ -121,19 +121,19 @@ TEST(CCDelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterRequestedTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - double interval = 1000.0 / 60.0; + double interval = 1.0 / 60.0; RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread); timer->setClient(&client); timer->setActive(true); // Run the first task, as that activates the timer and picks up a timebase. thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); - timer->setMonotonicallyIncreasingTimeMs(interval + 0.0001); + timer->setMonotonicallyIncreasingTime(interval + 0.0000001); thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); } // At 60Hz, when the tick returns at exactly 2*interval after the requested next time, make sure @@ -142,19 +142,19 @@ TEST(CCDelayBasedTimeSource, NextDelaySaneWhenExactlyTwiceAfterRequestedTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - double interval = 1000.0 / 60.0; + double interval = 1.0 / 60.0; RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread); timer->setClient(&client); timer->setActive(true); // Run the first task, as that activates the timer and picks up a timebase. thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); - timer->setMonotonicallyIncreasingTimeMs(2*interval); + timer->setMonotonicallyIncreasingTime(2*interval); thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); } // At 60Hz, when the tick returns at 2*interval and a bit after the requested next time, make sure @@ -163,19 +163,19 @@ TEST(CCDelayBasedTimeSource, NextDelaySaneWhenSlightlyAfterTwiceRequestedTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - double interval = 1000.0 / 60.0; + double interval = 1.0 / 60.0; RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread); timer->setClient(&client); timer->setActive(true); // Run the first task, as that activates the timer and picks up a timebase. thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); - timer->setMonotonicallyIncreasingTimeMs(2*interval + 0.0001); + timer->setMonotonicallyIncreasingTime(2*interval + 0.0000001); thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); } // At 60Hz, when the tick returns halfway to the next frame time, make sure @@ -184,19 +184,19 @@ TEST(CCDelayBasedTimeSource, NextDelaySaneWhenHalfAfterRequestedTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - double interval = 1000.0 / 60.0; + double interval = 1.0 / 60.0; RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(interval, &thread); timer->setClient(&client); timer->setActive(true); // Run the first task, as that activates the timer and picks up a timebase. thread.runPendingTask(); - EXPECT_EQ(16, thread.pendingDelay()); + EXPECT_EQ(16, thread.pendingDelayMs()); - timer->setMonotonicallyIncreasingTimeMs(interval + interval * 0.5); + timer->setMonotonicallyIncreasingTime(interval + interval * 0.5); thread.runPendingTask(); - EXPECT_EQ(8, thread.pendingDelay()); + EXPECT_EQ(8, thread.pendingDelayMs()); } @@ -206,31 +206,31 @@ TEST(CCDelayBasedTimeSourceTest, AchievesTargetRateWithNoNoise) FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); timer->setActive(true); double totalFrameTime = 0; for (int i = 0; i < numIterations; ++i) { - long long delay = thread.pendingDelay(); + long long delayMs = thread.pendingDelayMs(); // accumulate the "delay" - totalFrameTime += delay; + totalFrameTime += delayMs / 1000.0; // Run the callback exactly when asked - double now = timer->monotonicallyIncreasingTimeMs() + delay; - timer->setMonotonicallyIncreasingTimeMs(now); + double now = timer->monotonicallyIncreasingTime() + delayMs / 1000.0; + timer->setMonotonicallyIncreasingTime(now); thread.runPendingTask(); } double averageInterval = totalFrameTime / static_cast<double>(numIterations); - EXPECT_NEAR(1000.0 / 60.0, averageInterval, 0.1); + EXPECT_NEAR(1.0 / 60.0, averageInterval, 0.1); } TEST(CCDelayBasedTimeSource, TestDeactivateWhilePending) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); timer->setActive(true); // Should post a task. timer->setActive(false); @@ -242,12 +242,12 @@ TEST(CCDelayBasedTimeSource, TestDeactivateAndReactivateBeforeNextTickTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); // Should run the activate task, and pick up a new timebase. timer->setActive(true); - timer->setMonotonicallyIncreasingTimeMs(0); + timer->setMonotonicallyIncreasingTime(0); thread.runPendingTask(); // Stop the timer @@ -258,21 +258,21 @@ TEST(CCDelayBasedTimeSource, TestDeactivateAndReactivateBeforeNextTickTime) // Start the timer again, but before the next tick time the timer previously // planned on using. That same tick time should still be targeted. - timer->setMonotonicallyIncreasingTimeMs(4); + timer->setMonotonicallyIncreasingTime(0.004); timer->setActive(true); - EXPECT_EQ(12, thread.pendingDelay()); + EXPECT_EQ(12, thread.pendingDelayMs()); } TEST(CCDelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) { FakeCCThread thread; FakeCCTimeSourceClient client; - RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timer = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); timer->setClient(&client); // Should run the activate task, and pick up a new timebase. timer->setActive(true); - timer->setMonotonicallyIncreasingTimeMs(0); + timer->setMonotonicallyIncreasingTime(0); thread.runPendingTask(); // Stop the timer @@ -283,9 +283,9 @@ TEST(CCDelayBasedTimeSource, TestDeactivateAndReactivateAfterNextTickTime) // Start the timer again, but before the next tick time the timer previously // planned on using. That same tick time should still be targeted. - timer->setMonotonicallyIncreasingTimeMs(20); + timer->setMonotonicallyIncreasingTime(0.02); timer->setActive(true); - EXPECT_EQ(13, thread.pendingDelay()); + EXPECT_EQ(13, thread.pendingDelayMs()); } } diff --git a/Source/WebKit/chromium/tests/CCFrameRateControllerTest.cpp b/Source/WebKit/chromium/tests/CCFrameRateControllerTest.cpp index ae43d41c1..c9a60cfaa 100644 --- a/Source/WebKit/chromium/tests/CCFrameRateControllerTest.cpp +++ b/Source/WebKit/chromium/tests/CCFrameRateControllerTest.cpp @@ -39,13 +39,13 @@ class FakeCCFrameRateControllerClient : public WebCore::CCFrameRateControllerCli public: FakeCCFrameRateControllerClient() { reset(); } - void reset() { m_frameBegun = false; } - bool frameBegun() const { return m_frameBegun; } + void reset() { m_vsyncTicked = false; } + bool vsyncTicked() const { return m_vsyncTicked; } - virtual void beginFrame() { m_frameBegun = true; } + virtual void vsyncTick() { m_vsyncTicked = true; } protected: - bool m_frameBegun; + bool m_vsyncTicked; }; @@ -53,7 +53,7 @@ TEST(CCFrameRateControllerTest, TestFrameThrottling_ImmediateAck) { FakeCCThread thread; FakeCCFrameRateControllerClient client; - RefPtr<FakeCCDelayBasedTimeSource> timeSource = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timeSource = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); CCFrameRateController controller(timeSource); controller.setClient(&client); @@ -62,32 +62,32 @@ TEST(CCFrameRateControllerTest, TestFrameThrottling_ImmediateAck) double elapsed = 0; // Muck around with time a bit // Trigger one frame, make sure the vsync callback is called - elapsed += thread.pendingDelay(); - timeSource->setMonotonicallyIncreasingTimeMs(elapsed); + elapsed += thread.pendingDelayMs() / 1000.0; + timeSource->setMonotonicallyIncreasingTime(elapsed); thread.runPendingTask(); - EXPECT_TRUE(client.frameBegun()); + EXPECT_TRUE(client.vsyncTicked()); client.reset(); // Tell the controller we drew controller.didBeginFrame(); // Tell the controller the frame ended 5ms later - timeSource->setMonotonicallyIncreasingTimeMs(timeSource->monotonicallyIncreasingTimeMs() + 5); + timeSource->setMonotonicallyIncreasingTime(timeSource->monotonicallyIncreasingTime() + 0.005); controller.didFinishFrame(); // Trigger another frame, make sure vsync runs again - elapsed += thread.pendingDelay(); - EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTimeMs()); // Sanity check that previous code didn't move time backward. - timeSource->setMonotonicallyIncreasingTimeMs(elapsed); + elapsed += thread.pendingDelayMs() / 1000.0; + EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTime()); // Sanity check that previous code didn't move time backward. + timeSource->setMonotonicallyIncreasingTime(elapsed); thread.runPendingTask(); - EXPECT_TRUE(client.frameBegun()); + EXPECT_TRUE(client.vsyncTicked()); } TEST(CCFrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) { FakeCCThread thread; FakeCCFrameRateControllerClient client; - RefPtr<FakeCCDelayBasedTimeSource> timeSource = FakeCCDelayBasedTimeSource::create(1000.0 / 60.0, &thread); + RefPtr<FakeCCDelayBasedTimeSource> timeSource = FakeCCDelayBasedTimeSource::create(1.0 / 60.0, &thread); CCFrameRateController controller(timeSource); controller.setClient(&client); @@ -97,46 +97,46 @@ TEST(CCFrameRateControllerTest, TestFrameThrottling_TwoFramesInFlight) double elapsed = 0; // Muck around with time a bit // Trigger one frame, make sure the vsync callback is called - elapsed += thread.pendingDelay(); - timeSource->setMonotonicallyIncreasingTimeMs(elapsed); + elapsed += thread.pendingDelayMs() / 1000.0; + timeSource->setMonotonicallyIncreasingTime(elapsed); thread.runPendingTask(); - EXPECT_TRUE(client.frameBegun()); + EXPECT_TRUE(client.vsyncTicked()); client.reset(); // Tell the controller we drew controller.didBeginFrame(); // Trigger another frame, make sure vsync callback runs again - elapsed += thread.pendingDelay(); - EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTimeMs()); // Sanity check that previous code didn't move time backward. - timeSource->setMonotonicallyIncreasingTimeMs(elapsed); + elapsed += thread.pendingDelayMs() / 1000.0; + EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTime()); // Sanity check that previous code didn't move time backward. + timeSource->setMonotonicallyIncreasingTime(elapsed); thread.runPendingTask(); - EXPECT_TRUE(client.frameBegun()); + EXPECT_TRUE(client.vsyncTicked()); client.reset(); // Tell the controller we drew, again. controller.didBeginFrame(); // Trigger another frame. Since two frames are pending, we should not draw. - elapsed += thread.pendingDelay(); - EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTimeMs()); // Sanity check that previous code didn't move time backward. - timeSource->setMonotonicallyIncreasingTimeMs(elapsed); + elapsed += thread.pendingDelayMs() / 1000.0; + EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTime()); // Sanity check that previous code didn't move time backward. + timeSource->setMonotonicallyIncreasingTime(elapsed); thread.runPendingTask(); - EXPECT_FALSE(client.frameBegun()); + EXPECT_FALSE(client.vsyncTicked()); // Tell the controller the first frame ended 5ms later - timeSource->setMonotonicallyIncreasingTimeMs(timeSource->monotonicallyIncreasingTimeMs() + 5); + timeSource->setMonotonicallyIncreasingTime(timeSource->monotonicallyIncreasingTime() + 0.005); controller.didFinishFrame(); // Tick should not have been called - EXPECT_FALSE(client.frameBegun()); + EXPECT_FALSE(client.vsyncTicked()); // Trigger yet another frame. Since one frames is pending, another vsync callback should run. - elapsed += thread.pendingDelay(); - EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTimeMs()); // Sanity check that previous code didn't move time backward. - timeSource->setMonotonicallyIncreasingTimeMs(elapsed); + elapsed += thread.pendingDelayMs() / 1000.0; + EXPECT_TRUE(elapsed >= timeSource->monotonicallyIncreasingTime()); // Sanity check that previous code didn't move time backward. + timeSource->setMonotonicallyIncreasingTime(elapsed); thread.runPendingTask(); - EXPECT_TRUE(client.frameBegun()); + EXPECT_TRUE(client.vsyncTicked()); } } diff --git a/Source/WebKit/chromium/tests/CCLayerAnimationControllerImplTest.cpp b/Source/WebKit/chromium/tests/CCLayerAnimationControllerImplTest.cpp deleted file mode 100644 index e1aeccc53..000000000 --- a/Source/WebKit/chromium/tests/CCLayerAnimationControllerImplTest.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* - * 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 "CCAnimationTestCommon.h" -#include "cc/CCLayerAnimationControllerImpl.h" -#include "cc/CCAnimationCurve.h" -#include "cc/CCAnimationEvents.h" -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <wtf/Vector.h> - -using namespace WebCore; -using namespace WebKitTests; - -namespace { - -PassOwnPtr<CCActiveAnimation> createActiveAnimation(PassOwnPtr<CCAnimationCurve> curve, int id, CCActiveAnimation::TargetProperty property) -{ - return CCActiveAnimation::create(curve, 0, id, property); -} - -// Tests that transitioning opacity from 0 to 1 works as expected. -TEST(CCLayerAnimationControllerImplTest, TrivialTransition) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - - controller->add(toAdd.release()); - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(1, *events); - EXPECT_EQ(1, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests that two queued animations affecting the same property run in sequence. -TEST(CCLayerAnimationControllerImplTest, TrivialQueuing) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5f)), 2, CCActiveAnimation::Opacity)); - - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(1, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(1, dummy.opacity()); - controller->animate(2, *events); - EXPECT_EQ(0.5f, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests interrupting a transition with another transition. -TEST(CCLayerAnimationControllerImplTest, Interrupt) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5f)), 2, CCActiveAnimation::Opacity)); - toAdd->setRunState(CCActiveAnimation::WaitingForNextTick, 0); - controller->add(toAdd.release()); - - controller->animate(0.5, *events); // second anim starts NOW. - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(1, dummy.opacity()); - controller->animate(1.5, *events); - EXPECT_EQ(0.5f, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests scheduling two animations to run together when only one property is free. -TEST(CCLayerAnimationControllerImplTest, ScheduleTogetherWhenAPropertyIsBlocked) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 1, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 2, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 2, CCActiveAnimation::Opacity)); - - controller->animate(0, *events); - EXPECT_EQ(0, dummy.opacity()); - EXPECT_TRUE(controller->hasActiveAnimation()); - controller->animate(1, *events); - // Should not have started the float transition yet. - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - // The the float animation should have started at time 1 and should be done. - controller->animate(2, *events); - EXPECT_EQ(1, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests scheduling two animations to run together with different lengths and another -// animation queued to start when the shorter animation finishes (should wait -// for both to finish). -TEST(CCLayerAnimationControllerImplTest, ScheduleTogetherWithAnAnimWaiting) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(2)), 1, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5f)), 2, CCActiveAnimation::Opacity)); - - // Anims with id 1 should both start now. - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - // The opacity animation should have finished at time 1, but the group - // of animations with id 1 don't finish until time 2 because of the length - // of the transform animation. - controller->animate(2, *events); - // Should not have started the float transition yet. - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(1, dummy.opacity()); - - // The the second opacity animation should start at time 2 and should be - // done by time 3 - controller->animate(3, *events); - EXPECT_EQ(0.5f, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests scheduling an animation to start in the future. -TEST(CCLayerAnimationControllerImplTest, ScheduleAnimation) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); - toAdd->setStartTime(1); - controller->add(toAdd.release()); - - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(1, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(2, *events); - EXPECT_EQ(1, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests scheduling an animation to start in the future that's interrupting a running animation. -TEST(CCLayerAnimationControllerImplTest, ScheduledAnimationInterruptsRunningAnimation) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); - - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0.5f, 0)), 2, CCActiveAnimation::Opacity)); - toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); - toAdd->setStartTime(1); - controller->add(toAdd.release()); - - // First 2s opacity transition should start immediately. - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(0.5, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.25f, dummy.opacity()); - controller->animate(1, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.5f, dummy.opacity()); - controller->animate(2, *events); - EXPECT_EQ(0, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Tests scheduling an animation to start in the future that interrupts a running animation -// and there is yet another animation queued to start later. -TEST(CCLayerAnimationControllerImplTest, ScheduledAnimationInterruptsRunningAnimationWithAnimInQueue) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); - - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0.5f, 0)), 2, CCActiveAnimation::Opacity)); - toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); - toAdd->setStartTime(1); - controller->add(toAdd.release()); - - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 0.75f)), 3, CCActiveAnimation::Opacity)); - - // First 2s opacity transition should start immediately. - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(0.5, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.25f, dummy.opacity()); - EXPECT_TRUE(controller->hasActiveAnimation()); - controller->animate(1, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.5f, dummy.opacity()); - controller->animate(3, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(4, *events); - EXPECT_EQ(0.75f, dummy.opacity()); - EXPECT_FALSE(controller->hasActiveAnimation()); -} - -// Test that a looping animation loops and for the correct number of iterations. -TEST(CCLayerAnimationControllerImplTest, TrivialLooping) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); - toAdd->setIterations(3); - controller->add(toAdd.release()); - - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(1.25, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.25f, dummy.opacity()); - controller->animate(1.75, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); - controller->animate(2.25, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.25f, dummy.opacity()); - controller->animate(2.75, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); - controller->animate(3, *events); - EXPECT_FALSE(controller->hasActiveAnimation()); - EXPECT_EQ(1, dummy.opacity()); - - // Just be extra sure. - controller->animate(4, *events); - EXPECT_EQ(1, dummy.opacity()); -} - -// Test that an infinitely looping animation does indeed go until aborted. -TEST(CCLayerAnimationControllerImplTest, InfiniteLooping) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - const int id = 1; - OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); - toAdd->setIterations(-1); - controller->add(toAdd.release()); - - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(1.25, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.25f, dummy.opacity()); - controller->animate(1.75, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); - - controller->animate(1073741824.25, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.25f, dummy.opacity()); - controller->animate(1073741824.75, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); - - EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); - controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 0.75f); - EXPECT_FALSE(controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); -} - -// Test that pausing and resuming work as expected. -TEST(CCLayerAnimationControllerImplTest, PauseResume) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - const int id = 1; - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); - - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(0.5, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.5f, dummy.opacity()); - - EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); - controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Paused, 0.5f); - - controller->animate(1024, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.5f, dummy.opacity()); - - EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); - controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Running, 1024); - - controller->animate(1024.25, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); - controller->animate(1024.5, *events); - EXPECT_FALSE(controller->hasActiveAnimation()); - EXPECT_EQ(1, dummy.opacity()); -} - -TEST(CCLayerAnimationControllerImplTest, AbortAGroupedAnimation) -{ - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controller( - CCLayerAnimationControllerImpl::create(&dummy)); - - const int id = 1; - controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), id, CCActiveAnimation::Transform)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), id, CCActiveAnimation::Opacity)); - controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.75f)), 2, CCActiveAnimation::Opacity)); - - controller->animate(0, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0, dummy.opacity()); - controller->animate(1, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(0.5f, dummy.opacity()); - - EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); - controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 1); - controller->animate(1, *events); - EXPECT_TRUE(controller->hasActiveAnimation()); - EXPECT_EQ(1, dummy.opacity()); - controller->animate(2, *events); - EXPECT_TRUE(!controller->hasActiveAnimation()); - EXPECT_EQ(0.75f, dummy.opacity()); -} - -} // namespace diff --git a/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp b/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp index c4f103a3c..fa95a2ac5 100644 --- a/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerAnimationControllerTest.cpp @@ -56,7 +56,8 @@ PassOwnPtr<CCActiveAnimation> createActiveAnimation(PassOwnPtr<CCAnimationCurve> TEST(CCLayerAnimationControllerTest, createOpacityAnimation) { - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); const double duration = 1; WebCore::KeyframeValueList values(AnimatedPropertyOpacity); values.insert(new FloatAnimationValue(0, 0)); @@ -85,9 +86,37 @@ TEST(CCLayerAnimationControllerTest, createOpacityAnimation) EXPECT_EQ(1, curve->getValue(duration)); } +TEST(CCLayerAnimationControllerTest, ignoreUnsupportedAnimationDirections) +{ + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); + const double duration = 1; + WebCore::KeyframeValueList values(AnimatedPropertyOpacity); + values.insert(new FloatAnimationValue(0, 0)); + values.insert(new FloatAnimationValue(duration, 1)); + + RefPtr<Animation> animation = Animation::create(); + animation->setDuration(duration); + + IntSize boxSize; + + animation->setDirection(Animation::AnimationDirectionAlternate); + EXPECT_FALSE(controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0)); + + animation->setDirection(Animation::AnimationDirectionAlternateReverse); + EXPECT_FALSE(controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0)); + + animation->setDirection(Animation::AnimationDirectionReverse); + EXPECT_FALSE(controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0)); + + animation->setDirection(Animation::AnimationDirectionNormal); + EXPECT_TRUE(controller->addAnimation(values, boxSize, animation.get(), 0, 0, 0)); +} + TEST(CCLayerAnimationControllerTest, createTransformAnimation) { - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); const double duration = 1; WebCore::KeyframeValueList values(AnimatedPropertyWebkitTransform); @@ -124,92 +153,504 @@ TEST(CCLayerAnimationControllerTest, createTransformAnimation) TEST(CCLayerAnimationControllerTest, syncNewAnimation) { - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controllerImpl(CCLayerAnimationControllerImpl::create(&dummy)); - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummyImpl; + OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); - addOpacityTransitionToController(*controller, 1, 0, 1); + addOpacityTransitionToController(*controller, 1, 0, 1, false); - controller->synchronizeAnimations(controllerImpl.get()); + controller->pushAnimationUpdatesTo(controllerImpl.get()); EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); } -TEST(CCLayerAnimationControllerTest, syncAnimationProperties) +// If an animation is started on the impl thread before it is ticked on the main +// thread, we must be sure to respect the synchronized start time. +TEST(CCLayerAnimationControllerTest, doNotClobberStartTimes) { - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controllerImpl(CCLayerAnimationControllerImpl::create(&dummy)); - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummyImpl; + OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); - addOpacityTransitionToController(*controller, 1, 0, 1); + addOpacityTransitionToController(*controller, 1, 0, 1, false); - controller->synchronizeAnimations(controllerImpl.get()); + controller->pushAnimationUpdatesTo(controllerImpl.get()); EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); - // Push an animation property change to the impl thread (should not cause an animation to be added). - controller->pauseAnimation(0, 0); - controller->synchronizeAnimations(controllerImpl.get()); + CCAnimationEventsVector events; + controllerImpl->animate(1, &events); - EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); - EXPECT_EQ(CCActiveAnimation::Paused, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); + // Synchronize the start times. + EXPECT_EQ(1u, events.size()); + controller->notifyAnimationStarted(events[0]); + EXPECT_EQ(controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime(), controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime()); + + // Start the animation on the main thread. Should not affect the start time. + controller->animate(1.5, 0); + EXPECT_EQ(controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime(), controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->startTime()); } -TEST(CCLayerAnimationControllerTest, syncAbortedAnimation) +TEST(CCLayerAnimationControllerTest, syncPauseAndResume) { - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controllerImpl(CCLayerAnimationControllerImpl::create(&dummy)); - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummyImpl; + OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); - addOpacityTransitionToController(*controller, 1, 0, 1); + addOpacityTransitionToController(*controller, 1, 0, 1, false); - controller->synchronizeAnimations(controllerImpl.get()); + controller->pushAnimationUpdatesTo(controllerImpl.get()); EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); - controller->removeAnimation(0); + // Start the animations on each controller. + CCAnimationEventsVector events; + controllerImpl->animate(0, &events); + controller->animate(0, 0); + EXPECT_EQ(CCActiveAnimation::Running, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); + EXPECT_EQ(CCActiveAnimation::Running, controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); - // Abort an animation from the main thread. - controller->synchronizeAnimations(controllerImpl.get()); + // Pause the main-thread animation. + controller->suspendAnimations(1); + EXPECT_EQ(CCActiveAnimation::Paused, controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); - EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); + // The pause run state change should make it to the impl thread controller. + controller->pushAnimationUpdatesTo(controllerImpl.get()); + EXPECT_EQ(CCActiveAnimation::Paused, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); + + // Resume the main-thread animation. + controller->resumeAnimations(2); + EXPECT_EQ(CCActiveAnimation::Running, controller->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); + + // The pause run state change should make it to the impl thread controller. + controller->pushAnimationUpdatesTo(controllerImpl.get()); + EXPECT_EQ(CCActiveAnimation::Running, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); } -TEST(CCLayerAnimationControllerTest, syncCompletedAnimation) + +TEST(CCLayerAnimationControllerTest, doNotSyncFinishedAnimation) { - FakeLayerAnimationControllerImplClient dummy; - OwnPtr<CCLayerAnimationControllerImpl> controllerImpl(CCLayerAnimationControllerImpl::create(&dummy)); - OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummyImpl; + OwnPtr<CCLayerAnimationController> controllerImpl(CCLayerAnimationController::create(&dummyImpl)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller(CCLayerAnimationController::create(&dummy)); EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); - addOpacityTransitionToController(*controller, 1, 0, 1); + addOpacityTransitionToController(*controller, 1, 0, 1, false); - controller->synchronizeAnimations(controllerImpl.get()); + controller->pushAnimationUpdatesTo(controllerImpl.get()); EXPECT_TRUE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); EXPECT_EQ(CCActiveAnimation::WaitingForTargetAvailability, controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)->runState()); - // Step through the animation until it is finished. At the next sync, the main thread's animation should be cleared. - OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); - controllerImpl->animate(0, *events); - controllerImpl->animate(2, *events); + // Notify main thread controller that the animation has started. + CCAnimationEvent animationStartedEvent(CCAnimationEvent::Started, 0, 0, CCActiveAnimation::Opacity, 0); + controller->notifyAnimationStarted(animationStartedEvent); + + // Force animation to complete on impl thread. + controllerImpl->removeAnimation(0); EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); + + controller->pushAnimationUpdatesTo(controllerImpl.get()); + + // Even though the main thread has a 'new' animation, it should not be pushed because the animation has already completed on the impl thread. + EXPECT_FALSE(controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity)); +} + +// Tests that transitioning opacity from 0 to 1 works as expected. +TEST(CCLayerAnimationControllerTest, TrivialTransition) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + + controller->add(toAdd.release()); + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1, events.get()); + EXPECT_EQ(1, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests animations that are waiting for a synchronized start time do not finish. +TEST(CCLayerAnimationControllerTest, AnimationsWaitingForStartTimeDoNotFinishIfTheyWaitLongerToStartThanTheirDuration) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + toAdd->setNeedsSynchronizedStartTime(true); + + // We should pause at the first keyframe indefinitely waiting for that animation to start. + controller->add(toAdd.release()); + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(2, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + + // Send the synchronized start time. + controller->notifyAnimationStarted(CCAnimationEvent(CCAnimationEvent::Started, 0, 1, CCActiveAnimation::Opacity, 2)); + controller->animate(5, events.get()); + EXPECT_EQ(1, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests that two queued animations affecting the same property run in sequence. +TEST(CCLayerAnimationControllerTest, TrivialQueuing) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); + + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(1, dummy.opacity()); + controller->animate(2, events.get()); + EXPECT_EQ(0.5, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests interrupting a transition with another transition. +TEST(CCLayerAnimationControllerTest, Interrupt) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); + toAdd->setRunState(CCActiveAnimation::WaitingForNextTick, 0); + controller->add(toAdd.release()); + + // Since the animation was in the WaitingForNextTick state, it should start right in + // this call to animate. + controller->animate(0.5, events.get()); EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(1, dummy.opacity()); + controller->animate(1.5, events.get()); + EXPECT_EQ(0.5, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests scheduling two animations to run together when only one property is free. +TEST(CCLayerAnimationControllerTest, ScheduleTogetherWhenAPropertyIsBlocked) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); - controller->synchronizeAnimations(controllerImpl.get()); + controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 1, CCActiveAnimation::Transform)); + controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), 2, CCActiveAnimation::Transform)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 2, CCActiveAnimation::Opacity)); + controller->animate(0, events.get()); + EXPECT_EQ(0, dummy.opacity()); + EXPECT_TRUE(controller->hasActiveAnimation()); + controller->animate(1, events.get()); + // Should not have started the float transition yet. + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + // The float animation should have started at time 1 and should be done. + controller->animate(2, events.get()); + EXPECT_EQ(1, dummy.opacity()); EXPECT_FALSE(controller->hasActiveAnimation()); } +// Tests scheduling two animations to run together with different lengths and another +// animation queued to start when the shorter animation finishes (should wait +// for both to finish). +TEST(CCLayerAnimationControllerTest, ScheduleTogetherWithAnAnimWaiting) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(2)), 1, CCActiveAnimation::Transform)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.5)), 2, CCActiveAnimation::Opacity)); + + // Animations with id 1 should both start now. + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + // The opacity animation should have finished at time 1, but the group + // of animations with id 1 don't finish until time 2 because of the length + // of the transform animation. + controller->animate(2, events.get()); + // Should not have started the float transition yet. + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(1, dummy.opacity()); + + // The second opacity animation should start at time 2 and should be done by time 3 + controller->animate(3, events.get()); + EXPECT_EQ(0.5, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests scheduling an animation to start in the future. +TEST(CCLayerAnimationControllerTest, ScheduleAnimation) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); + toAdd->setStartTime(1); + controller->add(toAdd.release()); + + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(2, events.get()); + EXPECT_EQ(1, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests scheduling an animation to start in the future that's interrupting a running animation. +TEST(CCLayerAnimationControllerTest, ScheduledAnimationInterruptsRunningAnimation) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0.5, 0)), 2, CCActiveAnimation::Opacity)); + toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); + toAdd->setStartTime(1); + controller->add(toAdd.release()); + + // First 2s opacity transition should start immediately. + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(0.5, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.25, dummy.opacity()); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.5, dummy.opacity()); + controller->animate(2, events.get()); + EXPECT_EQ(0, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Tests scheduling an animation to start in the future that interrupts a running animation +// and there is yet another animation queued to start later. +TEST(CCLayerAnimationControllerTest, ScheduledAnimationInterruptsRunningAnimationWithAnimInQueue) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), 1, CCActiveAnimation::Opacity)); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0.5, 0)), 2, CCActiveAnimation::Opacity)); + toAdd->setRunState(CCActiveAnimation::WaitingForStartTime, 0); + toAdd->setStartTime(1); + controller->add(toAdd.release()); + + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 0.75)), 3, CCActiveAnimation::Opacity)); + + // First 2s opacity transition should start immediately. + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(0.5, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.25, dummy.opacity()); + EXPECT_TRUE(controller->hasActiveAnimation()); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.5, dummy.opacity()); + controller->animate(3, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(4, events.get()); + EXPECT_EQ(0.75, dummy.opacity()); + EXPECT_FALSE(controller->hasActiveAnimation()); +} + +// Test that a looping animation loops and for the correct number of iterations. +TEST(CCLayerAnimationControllerTest, TrivialLooping) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), 1, CCActiveAnimation::Opacity)); + toAdd->setIterations(3); + controller->add(toAdd.release()); + + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1.25, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.25, dummy.opacity()); + controller->animate(1.75, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); + controller->animate(2.25, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.25, dummy.opacity()); + controller->animate(2.75, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); + controller->animate(3, events.get()); + EXPECT_FALSE(controller->hasActiveAnimation()); + EXPECT_EQ(1, dummy.opacity()); + + // Just be extra sure. + controller->animate(4, events.get()); + EXPECT_EQ(1, dummy.opacity()); +} + +// Test that an infinitely looping animation does indeed go until aborted. +TEST(CCLayerAnimationControllerTest, InfiniteLooping) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + const int id = 1; + OwnPtr<CCActiveAnimation> toAdd(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); + toAdd->setIterations(-1); + controller->add(toAdd.release()); + + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1.25, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.25, dummy.opacity()); + controller->animate(1.75, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); + + controller->animate(1073741824.25, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.25, dummy.opacity()); + controller->animate(1073741824.75, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); + + EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); + controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 0.75); + EXPECT_FALSE(controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); +} + +// Test that pausing and resuming work as expected. +TEST(CCLayerAnimationControllerTest, PauseResume) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + const int id = 1; + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 0, 1)), id, CCActiveAnimation::Opacity)); + + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(0.5, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.5, dummy.opacity()); + + EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); + controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Paused, 0.5); + + controller->animate(1024, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.5, dummy.opacity()); + + EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); + controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Running, 1024); + + controller->animate(1024.25, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); + controller->animate(1024.5, events.get()); + EXPECT_FALSE(controller->hasActiveAnimation()); + EXPECT_EQ(1, dummy.opacity()); +} + +TEST(CCLayerAnimationControllerTest, AbortAGroupedAnimation) +{ + OwnPtr<CCAnimationEventsVector> events(adoptPtr(new CCAnimationEventsVector)); + FakeLayerAnimationControllerClient dummy; + OwnPtr<CCLayerAnimationController> controller( + CCLayerAnimationController::create(&dummy)); + + const int id = 1; + controller->add(createActiveAnimation(adoptPtr(new FakeTransformTransition(1)), id, CCActiveAnimation::Transform)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(2, 0, 1)), id, CCActiveAnimation::Opacity)); + controller->add(createActiveAnimation(adoptPtr(new FakeFloatTransition(1, 1, 0.75)), 2, CCActiveAnimation::Opacity)); + + controller->animate(0, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0, dummy.opacity()); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(0.5, dummy.opacity()); + + EXPECT_TRUE(controller->getActiveAnimation(id, CCActiveAnimation::Opacity)); + controller->getActiveAnimation(id, CCActiveAnimation::Opacity)->setRunState(CCActiveAnimation::Aborted, 1); + controller->animate(1, events.get()); + EXPECT_TRUE(controller->hasActiveAnimation()); + EXPECT_EQ(1, dummy.opacity()); + controller->animate(2, events.get()); + EXPECT_TRUE(!controller->hasActiveAnimation()); + EXPECT_EQ(0.75, dummy.opacity()); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCLayerImplTest.cpp b/Source/WebKit/chromium/tests/CCLayerImplTest.cpp index 8a9f35dc8..36c1082b7 100644 --- a/Source/WebKit/chromium/tests/CCLayerImplTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerImplTest.cpp @@ -81,7 +81,7 @@ TEST(CCLayerImplTest, verifyLayerChangesAreTrackedProperly) IntSize arbitraryIntSize = IntSize(111, 222); IntPoint arbitraryIntPoint = IntPoint(333, 444); IntRect arbitraryIntRect = IntRect(arbitraryIntPoint, arbitraryIntSize); - FloatRect arbitraryFloatRect = FloatRect(arbitraryFloatPoint, FloatSize(1.234, 5.678)); + FloatRect arbitraryFloatRect = FloatRect(arbitraryFloatPoint, FloatSize(1.234f, 5.678f)); Color arbitraryColor = Color(10, 20, 30); TransformationMatrix arbitraryTransform; arbitraryTransform.scale3d(0.1, 0.2, 0.3); @@ -112,7 +112,7 @@ TEST(CCLayerImplTest, verifyLayerChangesAreTrackedProperly) EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setDebugBorderWidth(arbitraryNumber)); EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setDrawsContent(true)); EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundColor(Color::gray)); - EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundCoversViewport(true)); + EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED(root->setBackgroundFilters(arbitraryFilters)); // Special case: check that sublayer transform changes all layer's descendants, but not the layer itself. root->resetAllChangeTrackingForSubtree(); diff --git a/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp b/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp index 2f5a7a25d..f5eae7f84 100644 --- a/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerIteratorTest.cpp @@ -47,7 +47,7 @@ public: int m_countRepresentingContributingSurface; int m_countRepresentingItself; - virtual bool drawsContent() const { return m_drawsContent; } + virtual bool drawsContent() const OVERRIDE { return m_drawsContent; } void setDrawsContent(bool drawsContent) { m_drawsContent = drawsContent; } private: diff --git a/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp b/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp index a533c9373..65db549ba 100644 --- a/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerSorterTest.cpp @@ -27,6 +27,7 @@ #include "cc/CCLayerSorter.h" #include "cc/CCLayerImpl.h" +#include "cc/CCMathUtil.h" #include "cc/CCSingleThreadProxy.h" #include <gtest/gtest.h> @@ -34,81 +35,94 @@ using namespace WebCore; namespace { -TEST(CCLayerSorterTest, PointInTriangle) +// Note: In the following overlap tests, the "camera" is looking down the negative Z axis, +// meaning that layers with smaller z values (more negative) are further from the camera +// and therefore must be drawn before layers with higher z values. + +TEST(CCLayerSorterTest, BasicOverlap) { - FloatPoint a(10.0, 10.0); - FloatPoint b(30.0, 10.0); - FloatPoint c(20.0, 20.0); - - // Point in the center is in the triangle. - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), a, b, c)); - - // Permuting the corners doesn't change the result. - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), a, c, b)); - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), b, a, c)); - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), b, c, a)); - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), c, a, b)); - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 15.0), c, b, a)); - - // Points on the edges are not in the triangle. - EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 10.0), a, b, c)); - EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(15.0, 15.0), a, b, c)); - EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(25.0, 15.0), a, b, c)); - - // Points just inside the edges are in the triangle. - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(20.0, 10.01), a, b, c)); - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(15.01, 15.0), a, b, c)); - EXPECT_TRUE(CCLayerSorter::pointInTriangle(FloatPoint(24.99, 15.0), a, b, c)); - - // Zero-area triangle doesn't intersect any point. - EXPECT_FALSE(CCLayerSorter::pointInTriangle(FloatPoint(15.0, 10.0), a, b, FloatPoint(20.0, 10.0))); + CCLayerSorter::ABCompareResult overlapResult; + const float zThreshold = 0.1f; + float weight = 0; + + // Trivial test, with one layer directly obscuring the other. + TransformationMatrix neg4Translate; + neg4Translate.translate3d(0, 0, -4); + CCLayerSorter::LayerShape front(2, 2, neg4Translate); + + TransformationMatrix neg5Translate; + neg5Translate.translate3d(0, 0, -5); + CCLayerSorter::LayerShape back(2, 2, neg5Translate); + + overlapResult = CCLayerSorter::checkOverlap(&front, &back, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::BBeforeA, overlapResult); + EXPECT_EQ(1, weight); + + overlapResult = CCLayerSorter::checkOverlap(&back, &front, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult); + EXPECT_EQ(1, weight); + + // One layer translated off to the right. No overlap should be detected. + TransformationMatrix rightTranslate; + rightTranslate.translate3d(10, 0, -5); + CCLayerSorter::LayerShape backRight(2, 2, rightTranslate); + overlapResult = CCLayerSorter::checkOverlap(&front, &backRight, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::None, overlapResult); + + // When comparing a layer with itself, z difference is always 0. + overlapResult = CCLayerSorter::checkOverlap(&front, &front, zThreshold, weight); + EXPECT_EQ(0, weight); } -TEST(CCLayerSorterTest, CalculateZDiff) +TEST(CCLayerSorterTest, RightAngleOverlap) { - // This should be bigger than the range of z values used. - const float threshold = 10.0; + CCLayerSorter::ABCompareResult overlapResult; + const float zThreshold = 0.1f; + float weight = 0; + + TransformationMatrix perspectiveMatrix; + perspectiveMatrix.applyPerspective(1000); + + // Two layers forming a right angle with a perspective viewing transform. + TransformationMatrix leftFaceMatrix; + leftFaceMatrix.rotate3d(0, 1, 0, -90).translateRight3d(-1, 0, -5); + CCLayerSorter::LayerShape leftFace(2, 2, perspectiveMatrix * leftFaceMatrix); + TransformationMatrix frontFaceMatrix; + frontFaceMatrix.translate3d(0, 0, -4); + CCLayerSorter::LayerShape frontFace(2, 2, perspectiveMatrix * frontFaceMatrix); + + overlapResult = CCLayerSorter::checkOverlap(&frontFace, &leftFace, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::BBeforeA, overlapResult); +} - // Trivial test, with one layer directly obscuring the other. +TEST(CCLayerSorterTest, IntersectingLayerOverlap) +{ + CCLayerSorter::ABCompareResult overlapResult; + const float zThreshold = 0.1f; + float weight = 0; + + TransformationMatrix perspectiveMatrix; + perspectiveMatrix.applyPerspective(1000); + + // Intersecting layers. An explicit order will be returned based on relative z + // values at the overlapping features but the weight returned should be zero. + TransformationMatrix frontFaceMatrix; + frontFaceMatrix.translate3d(0, 0, -4); + CCLayerSorter::LayerShape frontFace(2, 2, perspectiveMatrix * frontFaceMatrix); + + TransformationMatrix throughMatrix; + throughMatrix.rotate3d(0, 1, 0, 45).translateRight3d(0, 0, -4); + CCLayerSorter::LayerShape rotatedFace(2, 2, perspectiveMatrix * throughMatrix); + overlapResult = CCLayerSorter::checkOverlap(&frontFace, &rotatedFace, zThreshold, weight); + EXPECT_NE(CCLayerSorter::None, overlapResult); + EXPECT_EQ(0, weight); +} - CCLayerSorter::LayerShape front( - FloatPoint3D(-1.0, 1.0, 5.0), - FloatPoint3D(1.0, 1.0, 5.0), - FloatPoint3D(1.0, -1.0, 5.0), - FloatPoint3D(-1.0, -1.0, 5.0)); - - CCLayerSorter::LayerShape back( - FloatPoint3D(-1.0, 1.0, 4.0), - FloatPoint3D(1.0, 1.0, 4.0), - FloatPoint3D(1.0, -1.0, 4.0), - FloatPoint3D(-1.0, -1.0, 4.0)); - - EXPECT_GT(CCLayerSorter::calculateZDiff(front, back, threshold), 0.0); - EXPECT_LT(CCLayerSorter::calculateZDiff(back, front, threshold), 0.0); - - // When comparing a layer with itself, zDiff is always 0. - EXPECT_EQ(CCLayerSorter::calculateZDiff(front, front, threshold), 0.0); - EXPECT_EQ(CCLayerSorter::calculateZDiff(back, back, threshold), 0.0); - - // Same again but with two layers that intersect only at one point (0,0). - // This *does* count as obscuring, so we should get the same results. - - front = CCLayerSorter::LayerShape( - FloatPoint3D(-1.0, 0.0, 5.0), - FloatPoint3D(0.0, 0.0, 5.0), - FloatPoint3D(0.0, -1.0, 5.0), - FloatPoint3D(-1.0, -1.0, 5.0)); - - back = CCLayerSorter::LayerShape( - FloatPoint3D(0.0, 1.0, 4.0), - FloatPoint3D(1.0, 1.0, 4.0), - FloatPoint3D(1.0, 0.0, 4.0), - FloatPoint3D(0.0, 0.0, 4.0)); - - EXPECT_GT(CCLayerSorter::calculateZDiff(front, back, threshold), 0.0); - EXPECT_LT(CCLayerSorter::calculateZDiff(back, front, threshold), 0.0); - EXPECT_EQ(CCLayerSorter::calculateZDiff(front, front, threshold), 0.0); - EXPECT_EQ(CCLayerSorter::calculateZDiff(back, back, threshold), 0.0); +TEST(CCLayerSorterTest, LayersAtAngleOverlap) +{ + CCLayerSorter::ABCompareResult overlapResult; + const float zThreshold = 0.1f; + float weight = 0; // Trickier test with layers at an angle. // @@ -120,42 +134,64 @@ TEST(CCLayerSorterTest, CalculateZDiff) // +z / // // C is in front of A and behind B (not what you'd expect by comparing centers). - // A and B don't overlap, so they're incomparable (zDiff = 0). - - const float yHi = 10.0; - const float yLo = -10.0; - const float zA = 1.0; - const float zB = -1.0; - - CCLayerSorter::LayerShape layerA( - FloatPoint3D(-10.0, yHi, zA), - FloatPoint3D(-2.0, yHi, zA), - FloatPoint3D(-2.0, yLo, zA), - FloatPoint3D(-10.0, yLo, zA)); - - CCLayerSorter::LayerShape layerB( - FloatPoint3D(2.0, yHi, zB), - FloatPoint3D(10.0, yHi, zB), - FloatPoint3D(10.0, yLo, zB), - FloatPoint3D(2.0, yLo, zB)); - - CCLayerSorter::LayerShape layerC( - FloatPoint3D(-5.0, yHi, 5.0), - FloatPoint3D(5.0, yHi, -5.0), - FloatPoint3D(5.0, yLo, -5.0), - FloatPoint3D(-5.0, yLo, 5.0)); - - EXPECT_EQ(CCLayerSorter::calculateZDiff(layerA, layerA, threshold), 0.0); - EXPECT_EQ(CCLayerSorter::calculateZDiff(layerA, layerB, threshold), 0.0); - EXPECT_LT(CCLayerSorter::calculateZDiff(layerA, layerC, threshold), 0.0); - - EXPECT_EQ(CCLayerSorter::calculateZDiff(layerB, layerA, threshold), 0.0); - EXPECT_EQ(CCLayerSorter::calculateZDiff(layerB, layerB, threshold), 0.0); - EXPECT_GT(CCLayerSorter::calculateZDiff(layerB, layerC, threshold), 0.0); - - EXPECT_GT(CCLayerSorter::calculateZDiff(layerC, layerA, threshold), 0.0); - EXPECT_LT(CCLayerSorter::calculateZDiff(layerC, layerB, threshold), 0.0); - EXPECT_EQ(CCLayerSorter::calculateZDiff(layerC, layerC, threshold), 0.0); + // A and B don't overlap, so they're incomparable. + + TransformationMatrix transformA; + transformA.translate3d(-6, 0, 1); + CCLayerSorter::LayerShape layerA(8, 20, transformA); + + TransformationMatrix transformB; + transformB.translate3d(6, 0, -1); + CCLayerSorter::LayerShape layerB(8, 20, transformB); + + TransformationMatrix transformC; + transformC.rotate3d(0, 1, 0, 40); + CCLayerSorter::LayerShape layerC(8, 20, transformC); + + overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerC, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult); + overlapResult = CCLayerSorter::checkOverlap(&layerC, &layerB, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult); + overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerB, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::None, overlapResult); +} + +TEST(CCLayerSorterTest, LayersUnderPathologicalPerspectiveTransform) +{ + CCLayerSorter::ABCompareResult overlapResult; + const float zThreshold = 0.1f; + float weight = 0; + + // On perspective projection, if w becomes negative, the re-projected point will be + // invalid and un-usable. Correct code needs to clip away portions of the geometry + // where w < 0. If the code uses the invalid value, it will think that a layer has + // different bounds than it really does, which can cause things to sort incorrectly. + + TransformationMatrix perspectiveMatrix; + perspectiveMatrix.applyPerspective(1); + + TransformationMatrix transformA; + transformA.translate3d(-15, 0, -2); + CCLayerSorter::LayerShape layerA(10, 10, perspectiveMatrix * transformA); + + // With this sequence of transforms, when layer B is correctly clipped, it will be + // visible on the left half of the projection plane, in front of layerA. When it is + // not clipped, its bounds will actually incorrectly appear much smaller and the + // correct sorting dependency will not be found. + TransformationMatrix transformB; + transformB.translate3d(0, 0, 0.7); + transformB.rotate3d(0, 45, 0); + CCLayerSorter::LayerShape layerB(10, 10, perspectiveMatrix * transformB); + + // Sanity check that the test case actually covers the intended scenario, where part + // of layer B go behind the w = 0 plane. + FloatQuad testQuad = FloatQuad(FloatRect(FloatPoint(-0.5, -0.5), FloatSize(1, 1))); + bool clipped = false; + CCMathUtil::mapQuad(perspectiveMatrix * transformB, testQuad, clipped); + ASSERT_TRUE(clipped); + + overlapResult = CCLayerSorter::checkOverlap(&layerA, &layerB, zThreshold, weight); + EXPECT_EQ(CCLayerSorter::ABeforeB, overlapResult); } TEST(CCLayerSorterTest, verifyExistingOrderingPreservedWhenNoZDiff) diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp index e6206cc58..51a032618 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostCommonTest.cpp @@ -26,14 +26,19 @@ #include "cc/CCLayerTreeHostCommon.h" +#include "CCAnimationTestCommon.h" #include "CCLayerTreeTestCommon.h" #include "LayerChromium.h" #include "TransformationMatrix.h" +#include "TranslateTransformOperation.h" +#include "cc/CCLayerAnimationController.h" +#include "cc/CCMathUtil.h" #include <gmock/gmock.h> #include <gtest/gtest.h> using namespace WebCore; +using namespace WebKitTests; namespace { @@ -76,7 +81,7 @@ public: { } - virtual bool drawsContent() const { return true; } + virtual bool drawsContent() const OVERRIDE { return true; } }; TEST(CCLayerTreeHostCommonTest, verifyTransformsForNoOpLayer) @@ -308,6 +313,56 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForSingleRenderSurface) EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->originTransform()); EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->drawTransform()); + // The screen space is the same as the target since the child surface draws into the root. + EXPECT_TRANSFORMATION_MATRIX_EQ(parentCompositeTransform, child->targetRenderSurface()->screenSpaceTransform()); +} + +TEST(CCLayerTreeHostCommonTest, verifyTransformsForReplica) +{ + RefPtr<LayerChromium> parent = LayerChromium::create(); + RefPtr<LayerChromium> child = LayerChromium::create(); + RefPtr<LayerChromium> childReplica = LayerChromium::create(); + RefPtr<LayerChromiumWithForcedDrawsContent> grandChild = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(grandChild); + child->setReplicaLayer(childReplica.get()); + + // Child is set up so that a new render surface should be created. + child->setOpacity(0.5f); + + TransformationMatrix identityMatrix; + TransformationMatrix parentLayerTransform; + parentLayerTransform.scale3d(2.0, 2.0, 1.0); + TransformationMatrix parentTranslationToAnchor; + parentTranslationToAnchor.translate(2.5, 3.0); + TransformationMatrix parentSublayerMatrix; + parentSublayerMatrix.scale3d(10.0, 10.0, 3.3); + TransformationMatrix parentTranslationToCenter; + parentTranslationToCenter.translate(5.0, 6.0); + TransformationMatrix parentCompositeTransform = parentTranslationToAnchor * parentLayerTransform * parentTranslationToAnchor.inverse() + * parentTranslationToCenter * parentSublayerMatrix * parentTranslationToCenter.inverse(); + TransformationMatrix childTranslationToCenter; + childTranslationToCenter.translate(8.0, 9.0); + TransformationMatrix replicaLayerTransform; + replicaLayerTransform.scale3d(3.0, 3.0, 1.0); + TransformationMatrix replicaCompositeTransform = parentCompositeTransform * replicaLayerTransform; + + // Child's render surface should not exist yet. + ASSERT_FALSE(child->renderSurface()); + + setLayerPropertiesForTesting(parent.get(), parentLayerTransform, parentSublayerMatrix, FloatPoint(0.25f, 0.25f), FloatPoint(2.5f, 3.0f), IntSize(10, 12), false); + setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(0.0f, 0.0f), IntSize(16, 18), false); + setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(-0.5f, -0.5f), IntSize(1, 1), false); + setLayerPropertiesForTesting(childReplica.get(), replicaLayerTransform, identityMatrix, FloatPoint(0.0f, 0.0f), FloatPoint(0.0f, 0.0f), IntSize(0, 0), false); + executeCalculateDrawTransformsAndVisibility(parent.get()); + + // Render surface should have been created now. + ASSERT_TRUE(child->renderSurface()); + ASSERT_EQ(child->renderSurface(), child->targetRenderSurface()); + + EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->targetRenderSurface()->replicaOriginTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(replicaCompositeTransform, child->targetRenderSurface()->replicaScreenSpaceTransform()); } TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) @@ -315,6 +370,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) // This test creates a more complex tree and verifies it all at once. This covers the following cases: // - layers that are described w.r.t. a render surface: should have draw transforms described w.r.t. that surface // - A render surface described w.r.t. an ancestor render surface: should have a draw transform described w.r.t. that ancestor surface + // - Replicas of a render surface are described w.r.t. the replica's transform around its anchor, along with the surface itself. // - Sanity check on recursion: verify transforms of layers described w.r.t. a render surface that is described w.r.t. an ancestor render surface. // - verifying that each layer has a reference to the correct renderSurface and targetRenderSurface values. @@ -324,6 +380,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) RefPtr<LayerChromium> childOfRoot = LayerChromium::create(); RefPtr<LayerChromium> childOfRS1 = LayerChromium::create(); RefPtr<LayerChromium> childOfRS2 = LayerChromium::create(); + RefPtr<LayerChromium> replicaOfRS1 = LayerChromium::create(); + RefPtr<LayerChromium> replicaOfRS2 = LayerChromium::create(); RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create(); RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); @@ -336,6 +394,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) childOfRoot->addChild(grandChildOfRoot); childOfRS1->addChild(grandChildOfRS1); childOfRS2->addChild(grandChildOfRS2); + renderSurface1->setReplicaLayer(replicaOfRS1.get()); + renderSurface2->setReplicaLayer(replicaOfRS2.get()); // In combination with descendantDrawsContent, opacity != 1 forces the layer to have a new renderSurface. renderSurface1->setOpacity(0.5f); @@ -344,6 +404,7 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) // All layers in the tree are initialized with an anchor at 2.5 and a size of (10,10). // matrix "A" is the composite layer transform used in all layers, centered about the anchor point // matrix "B" is the sublayer transform used in all layers, centered about the center position of the layer. + // matrix "R" is the composite replica transform used in all replica layers. // // x component tests that layerTransform and sublayerTransform are done in the right order (translation and scale are noncommutative). // y component has a translation by 1.0 for every ancestor, which indicates the "depth" of the layer in the hierarchy. @@ -355,9 +416,12 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) layerTransform.translate(1.0, 1.0); TransformationMatrix sublayerTransform; sublayerTransform.scale3d(10.0, 1.0, 1.0); + TransformationMatrix replicaLayerTransform; + replicaLayerTransform.scale3d(-2.0, 5.0, 1.0); TransformationMatrix A = translationToAnchor * layerTransform * translationToAnchor.inverse(); TransformationMatrix B = translationToCenter * sublayerTransform * translationToCenter.inverse(); + TransformationMatrix R = A * translationToAnchor * replicaLayerTransform * translationToAnchor.inverse(); setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); @@ -368,6 +432,8 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(replicaOfRS1.get(), replicaLayerTransform, sublayerTransform, FloatPoint(), FloatPoint(2.5f, 0.0f), IntSize(), false); + setLayerPropertiesForTesting(replicaOfRS2.get(), replicaLayerTransform, sublayerTransform, FloatPoint(), FloatPoint(2.5f, 0.0f), IntSize(), false); executeCalculateDrawTransformsAndVisibility(parent.get()); @@ -433,8 +499,14 @@ TEST(CCLayerTreeHostCommonTest, verifyTransformsForRenderSurfaceHierarchy) // // Origin transform of render surface 1 is described with respect to root. EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaOriginTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A, renderSurface1->renderSurface()->screenSpaceTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * R, renderSurface1->renderSurface()->replicaScreenSpaceTransform()); // Origin transform of render surface 2 is described with respect to render surface 2. EXPECT_TRANSFORMATION_MATRIX_EQ(B * A, renderSurface2->renderSurface()->originTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(B * R, renderSurface2->renderSurface()->replicaOriginTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * A, renderSurface2->renderSurface()->screenSpaceTransform()); + EXPECT_TRANSFORMATION_MATRIX_EQ(A * B * A * B * R, renderSurface2->renderSurface()->replicaScreenSpaceTransform()); // Sanity check. If these fail there is probably a bug in the test itself. // It is expected that we correctly set up transforms so that the y-component of the screen-space transform @@ -457,7 +529,7 @@ TEST(CCLayerTreeHostCommonTest, verifyRenderSurfaceListForClipLayer) RefPtr<LayerChromium> parent = LayerChromium::create(); RefPtr<LayerChromium> renderSurface1 = LayerChromium::create(); RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent()); - renderSurface1->setOpacity(0.9); + renderSurface1->setOpacity(0.9f); const TransformationMatrix identityMatrix; setLayerPropertiesForTesting(renderSurface1.get(), identityMatrix, identityMatrix, FloatPoint::zero(), FloatPoint::zero(), IntSize(10, 10), false); @@ -550,9 +622,58 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces) setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false); child->setMasksToBounds(true); - child->setOpacity(0.4); - grandChild->setOpacity(0.5); - greatGrandChild->setOpacity(0.4); + child->setOpacity(0.4f); + grandChild->setOpacity(0.5f); + greatGrandChild->setOpacity(0.4f); + + Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; + Vector<RefPtr<LayerChromium> > dummyLayerList; + int dummyMaxTextureSize = 512; + + // FIXME: when we fix this "root-layer special case" behavior in CCLayerTreeHost, we will have to fix it here, too. + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent.get()); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + ASSERT_EQ(2U, renderSurfaceLayerList.size()); + EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id()); + EXPECT_EQ(child->id(), renderSurfaceLayerList[1]->id()); +} + +TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfacesCrashRepro) +{ + // This is a similar situation as verifyClipRectCullsRenderSurfaces, except that + // it reproduces a crash bug http://code.google.com/p/chromium/issues/detail?id=106734. + + const TransformationMatrix identityMatrix; + RefPtr<LayerChromium> parent = LayerChromium::create(); + RefPtr<LayerChromium> child = LayerChromium::create(); + RefPtr<LayerChromium> grandChild = LayerChromium::create(); + RefPtr<LayerChromium> greatGrandChild = LayerChromium::create(); + RefPtr<LayerChromiumWithForcedDrawsContent> leafNode1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> leafNode2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(grandChild); + grandChild->addChild(greatGrandChild); + + // leafNode1 ensures that parent and child are kept on the renderSurfaceLayerList, + // even though grandChild and greatGrandChild should be clipped. + child->addChild(leafNode1); + greatGrandChild->addChild(leafNode2); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false); + setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false); + setLayerPropertiesForTesting(grandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false); + setLayerPropertiesForTesting(greatGrandChild.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false); + setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false); + setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false); + + child->setMasksToBounds(true); + child->setOpacity(0.4f); + grandChild->setOpacity(0.5f); + greatGrandChild->setOpacity(0.4f); // Contaminate the grandChild and greatGrandChild's clipRect to reproduce the crash // bug found in http://code.google.com/p/chromium/issues/detail?id=106734. In this @@ -561,9 +682,6 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces) // renderSurface remains on the renderSurfaceLayerList, which violates the assumption // that an empty renderSurface will always be the last item on the list, which // ultimately caused the crash. - // - // FIXME: it is also useful to test with this commented out. Eventually we should - // create several test cases that test clipRect/drawableContentRect computation. child->setClipRect(IntRect(IntPoint::zero(), IntSize(20, 20))); greatGrandChild->setClipRect(IntRect(IntPoint::zero(), IntSize(1234, 1234))); @@ -582,6 +700,631 @@ TEST(CCLayerTreeHostCommonTest, verifyClipRectCullsRenderSurfaces) EXPECT_EQ(child->id(), renderSurfaceLayerList[1]->id()); } +TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToLayers) +{ + // Verify that layers get the appropriate clipRects when their parent masksToBounds is true. + // + // grandChild1 - completely inside the region; clipRect should be the mask region (larger than this layer's bounds). + // grandChild2 - partially clipped but NOT masksToBounds; the clipRect should be the parent's clipRect regardless of the layer's bounds. + // grandChild3 - partially clipped and masksToBounds; the clipRect will be the intersection of layerBounds and the mask region. + // grandChild4 - outside parent's clipRect, and masksToBounds; the clipRect should be empty. + // + + const TransformationMatrix identityMatrix; + RefPtr<LayerChromium> parent = LayerChromium::create(); + RefPtr<LayerChromium> child = LayerChromium::create(); + RefPtr<LayerChromium> grandChild1 = LayerChromium::create(); + RefPtr<LayerChromium> grandChild2 = LayerChromium::create(); + RefPtr<LayerChromium> grandChild3 = LayerChromium::create(); + RefPtr<LayerChromium> grandChild4 = LayerChromium::create(); + + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(grandChild1); + child->addChild(grandChild2); + child->addChild(grandChild3); + child->addChild(grandChild4); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false); + setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false); + setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(5, 5), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChild2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChild3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChild4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false); + + child->setMasksToBounds(true); + grandChild3->setMasksToBounds(true); + grandChild4->setMasksToBounds(true); + + // Force everyone to be a render surface. + child->setOpacity(0.4f); + grandChild1->setOpacity(0.5f); + grandChild2->setOpacity(0.5f); + grandChild3->setOpacity(0.5f); + grandChild4->setOpacity(0.5f); + + Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; + Vector<RefPtr<LayerChromium> > dummyLayerList; + int dummyMaxTextureSize = 512; + + // FIXME: when we fix this "root-layer special case" behavior in CCLayerTreeHost, we will have to fix it here, too. + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent.get()); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(20, 20)), grandChild1->clipRect()); + EXPECT_INT_RECT_EQ(IntRect(IntPoint::zero(), IntSize(20, 20)), grandChild2->clipRect()); + EXPECT_INT_RECT_EQ(IntRect(IntPoint(15, 15), IntSize(5, 5)), grandChild3->clipRect()); + EXPECT_TRUE(grandChild4->clipRect().isEmpty()); +} + +TEST(CCLayerTreeHostCommonTest, verifyClipRectIsPropagatedCorrectlyToSurfaces) +{ + // Verify that renderSurfaces (and their layers) get the appropriate clipRects when their parent masksToBounds is true. + // + // Layers that own renderSurfaces (at least for now) do not inherit any clipRect; + // instead the surface will enforce the clip for the entire subtree. They may still + // have a clipRect of their own layer bounds, however, if masksToBounds was true. + // + + const TransformationMatrix identityMatrix; + RefPtr<LayerChromium> parent = LayerChromium::create(); + RefPtr<LayerChromium> child = LayerChromium::create(); + RefPtr<LayerChromium> grandChild1 = LayerChromium::create(); + RefPtr<LayerChromium> grandChild2 = LayerChromium::create(); + RefPtr<LayerChromium> grandChild3 = LayerChromium::create(); + RefPtr<LayerChromium> grandChild4 = LayerChromium::create(); + RefPtr<LayerChromiumWithForcedDrawsContent> leafNode1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> leafNode2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> leafNode3 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> leafNode4 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + + parent->createRenderSurface(); + parent->addChild(child); + child->addChild(grandChild1); + child->addChild(grandChild2); + child->addChild(grandChild3); + child->addChild(grandChild4); + + // the leaf nodes ensure that these grandChildren become renderSurfaces for this test. + grandChild1->addChild(leafNode1); + grandChild2->addChild(leafNode2); + grandChild3->addChild(leafNode3); + grandChild4->addChild(leafNode4); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false); + setLayerPropertiesForTesting(child.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(20, 20), false); + setLayerPropertiesForTesting(grandChild1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(5, 5), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChild2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChild3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(15, 15), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChild4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(45, 45), IntSize(10, 10), false); + setLayerPropertiesForTesting(leafNode1.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false); + setLayerPropertiesForTesting(leafNode2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false); + setLayerPropertiesForTesting(leafNode3.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false); + setLayerPropertiesForTesting(leafNode4.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 10), false); + + child->setMasksToBounds(true); + grandChild3->setMasksToBounds(true); + grandChild4->setMasksToBounds(true); + + // Force everyone to be a render surface. + child->setOpacity(0.4f); + grandChild1->setOpacity(0.5f); + grandChild2->setOpacity(0.5f); + grandChild3->setOpacity(0.5f); + grandChild4->setOpacity(0.5f); + + Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; + Vector<RefPtr<LayerChromium> > dummyLayerList; + int dummyMaxTextureSize = 512; + + // FIXME: when we fix this "root-layer special case" behavior in CCLayerTreeHost, we will have to fix it here, too. + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent.get()); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + ASSERT_TRUE(grandChild1->renderSurface()); + ASSERT_TRUE(grandChild2->renderSurface()); + ASSERT_TRUE(grandChild3->renderSurface()); + EXPECT_FALSE(grandChild4->renderSurface()); // Because grandChild4 is entirely clipped, it is expected to not have a renderSurface. + + // Surfaces are clipped by their parent, but un-affected by the owning layer's masksToBounds. + EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild1->renderSurface()->clipRect()); + EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild2->renderSurface()->clipRect()); + EXPECT_INT_RECT_EQ(IntRect(IntPoint(0, 0), IntSize(20, 20)), grandChild3->renderSurface()->clipRect()); + + // Layers do not inherit the clipRect from their owned surfaces, but if masksToBounds is true, they do create their own clipRect. + EXPECT_FALSE(grandChild1->usesLayerClipping()); + EXPECT_FALSE(grandChild2->usesLayerClipping()); + EXPECT_TRUE(grandChild3->usesLayerClipping()); + EXPECT_TRUE(grandChild4->usesLayerClipping()); +} + +TEST(CCLayerTreeHostCommonTest, verifyAnimationsForRenderSurfaceHierarchy) +{ + RefPtr<LayerChromium> parent = LayerChromium::create(); + RefPtr<LayerChromium> renderSurface1 = LayerChromium::create(); + RefPtr<LayerChromium> renderSurface2 = LayerChromium::create(); + RefPtr<LayerChromium> childOfRoot = LayerChromium::create(); + RefPtr<LayerChromium> childOfRS1 = LayerChromium::create(); + RefPtr<LayerChromium> childOfRS2 = LayerChromium::create(); + RefPtr<LayerChromium> grandChildOfRoot = LayerChromium::create(); + RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> grandChildOfRS2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + parent->createRenderSurface(); + parent->addChild(renderSurface1); + parent->addChild(childOfRoot); + renderSurface1->addChild(childOfRS1); + renderSurface1->addChild(renderSurface2); + renderSurface2->addChild(childOfRS2); + childOfRoot->addChild(grandChildOfRoot); + childOfRS1->addChild(grandChildOfRS1); + childOfRS2->addChild(grandChildOfRS2); + + // In combination with descendantDrawsContent, opacity != 1 forces the layer to have a new renderSurface. + addOpacityTransitionToController(*renderSurface1->layerAnimationController(), 10, 1, 0, false); + + // Also put an animated opacity on a layer without descendants. + addOpacityTransitionToController(*grandChildOfRoot->layerAnimationController(), 10, 1, 0, false); + + TransformationMatrix layerTransform; + layerTransform.translate(1.0, 1.0); + TransformationMatrix sublayerTransform; + sublayerTransform.scale3d(10.0, 1.0, 1.0); + + // In combination with descendantDrawsContent, an animated transform forces the layer to have a new renderSurface. + addAnimatedTransformToController(*renderSurface2->layerAnimationController(), 10, 30, 0); + + // Also put transform animations on grandChildOfRoot, and grandChildOfRS2 + addAnimatedTransformToController(*grandChildOfRoot->layerAnimationController(), 10, 30, 0); + addAnimatedTransformToController(*grandChildOfRS2->layerAnimationController(), 10, 30, 0); + + setLayerPropertiesForTesting(parent.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(renderSurface1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(renderSurface2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(childOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(childOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(childOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChildOfRoot.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChildOfRS1.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + setLayerPropertiesForTesting(grandChildOfRS2.get(), layerTransform, sublayerTransform, FloatPoint(0.25f, 0.0f), FloatPoint(2.5f, 0.0f), IntSize(10, 10), false); + + executeCalculateDrawTransformsAndVisibility(parent.get()); + + // Only layers that are associated with render surfaces should have an actual renderSurface() value. + // + ASSERT_TRUE(parent->renderSurface()); + ASSERT_FALSE(childOfRoot->renderSurface()); + ASSERT_FALSE(grandChildOfRoot->renderSurface()); + + ASSERT_TRUE(renderSurface1->renderSurface()); + ASSERT_FALSE(childOfRS1->renderSurface()); + ASSERT_FALSE(grandChildOfRS1->renderSurface()); + + ASSERT_TRUE(renderSurface2->renderSurface()); + ASSERT_FALSE(childOfRS2->renderSurface()); + ASSERT_FALSE(grandChildOfRS2->renderSurface()); + + // Verify all targetRenderSurface accessors + // + EXPECT_EQ(parent->renderSurface(), parent->targetRenderSurface()); + EXPECT_EQ(parent->renderSurface(), childOfRoot->targetRenderSurface()); + EXPECT_EQ(parent->renderSurface(), grandChildOfRoot->targetRenderSurface()); + + EXPECT_EQ(renderSurface1->renderSurface(), renderSurface1->targetRenderSurface()); + EXPECT_EQ(renderSurface1->renderSurface(), childOfRS1->targetRenderSurface()); + EXPECT_EQ(renderSurface1->renderSurface(), grandChildOfRS1->targetRenderSurface()); + + EXPECT_EQ(renderSurface2->renderSurface(), renderSurface2->targetRenderSurface()); + EXPECT_EQ(renderSurface2->renderSurface(), childOfRS2->targetRenderSurface()); + EXPECT_EQ(renderSurface2->renderSurface(), grandChildOfRS2->targetRenderSurface()); + + // Verify drawOpacityIsAnimating values + // + EXPECT_FALSE(parent->drawOpacityIsAnimating()); + EXPECT_FALSE(childOfRoot->drawOpacityIsAnimating()); + EXPECT_TRUE(grandChildOfRoot->drawOpacityIsAnimating()); + EXPECT_FALSE(renderSurface1->drawOpacityIsAnimating()); + EXPECT_TRUE(renderSurface1->renderSurface()->drawOpacityIsAnimating()); + EXPECT_FALSE(childOfRS1->drawOpacityIsAnimating()); + EXPECT_FALSE(grandChildOfRS1->drawOpacityIsAnimating()); + EXPECT_FALSE(renderSurface2->drawOpacityIsAnimating()); + EXPECT_FALSE(renderSurface2->renderSurface()->drawOpacityIsAnimating()); + EXPECT_FALSE(childOfRS2->drawOpacityIsAnimating()); + EXPECT_FALSE(grandChildOfRS2->drawOpacityIsAnimating()); + + // Verify drawTransformsAnimatingInTarget values + // + EXPECT_FALSE(parent->drawTransformIsAnimating()); + EXPECT_FALSE(childOfRoot->drawTransformIsAnimating()); + EXPECT_TRUE(grandChildOfRoot->drawTransformIsAnimating()); + EXPECT_FALSE(renderSurface1->drawTransformIsAnimating()); + EXPECT_FALSE(renderSurface1->renderSurface()->targetSurfaceTransformsAreAnimating()); + EXPECT_FALSE(childOfRS1->drawTransformIsAnimating()); + EXPECT_FALSE(grandChildOfRS1->drawTransformIsAnimating()); + EXPECT_FALSE(renderSurface2->drawTransformIsAnimating()); + EXPECT_TRUE(renderSurface2->renderSurface()->targetSurfaceTransformsAreAnimating()); + EXPECT_FALSE(childOfRS2->drawTransformIsAnimating()); + EXPECT_TRUE(grandChildOfRS2->drawTransformIsAnimating()); + + // Verify drawTransformsAnimatingInScreen values + // + EXPECT_FALSE(parent->screenSpaceTransformIsAnimating()); + EXPECT_FALSE(childOfRoot->screenSpaceTransformIsAnimating()); + EXPECT_TRUE(grandChildOfRoot->screenSpaceTransformIsAnimating()); + EXPECT_FALSE(renderSurface1->screenSpaceTransformIsAnimating()); + EXPECT_FALSE(renderSurface1->renderSurface()->screenSpaceTransformsAreAnimating()); + EXPECT_FALSE(childOfRS1->screenSpaceTransformIsAnimating()); + EXPECT_FALSE(grandChildOfRS1->screenSpaceTransformIsAnimating()); + EXPECT_TRUE(renderSurface2->screenSpaceTransformIsAnimating()); + EXPECT_TRUE(renderSurface2->renderSurface()->screenSpaceTransformsAreAnimating()); + EXPECT_TRUE(childOfRS2->screenSpaceTransformIsAnimating()); + EXPECT_TRUE(grandChildOfRS2->screenSpaceTransformIsAnimating()); + + + // Sanity check. If these fail there is probably a bug in the test itself. + // It is expected that we correctly set up transforms so that the y-component of the screen-space transform + // encodes the "depth" of the layer in the tree. + EXPECT_FLOAT_EQ(1.0, parent->screenSpaceTransform().m42()); + EXPECT_FLOAT_EQ(2.0, childOfRoot->screenSpaceTransform().m42()); + EXPECT_FLOAT_EQ(3.0, grandChildOfRoot->screenSpaceTransform().m42()); + + EXPECT_FLOAT_EQ(2.0, renderSurface1->screenSpaceTransform().m42()); + EXPECT_FLOAT_EQ(3.0, childOfRS1->screenSpaceTransform().m42()); + EXPECT_FLOAT_EQ(4.0, grandChildOfRS1->screenSpaceTransform().m42()); + + EXPECT_FLOAT_EQ(3.0, renderSurface2->screenSpaceTransform().m42()); + EXPECT_FLOAT_EQ(4.0, childOfRS2->screenSpaceTransform().m42()); + EXPECT_FLOAT_EQ(5.0, grandChildOfRS2->screenSpaceTransform().m42()); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForIdentityTransform) +{ + // Test the calculateVisibleRect() function works correctly for identity transforms. + + IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + TransformationMatrix layerToSurfaceTransform; + + // Case 1: Layer is contained within the surface. + IntRect layerContentRect = IntRect(IntPoint(10, 10), IntSize(30, 30)); + IntRect expected = IntRect(IntPoint(10, 10), IntSize(30, 30)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); + + // Case 2: Layer is outside the surface rect. + layerContentRect = IntRect(IntPoint(120, 120), IntSize(30, 30)); + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_TRUE(actual.isEmpty()); + + // Case 3: Layer is partially overlapping the surface rect. + layerContentRect = IntRect(IntPoint(80, 80), IntSize(30, 30)); + expected = IntRect(IntPoint(80, 80), IntSize(20, 20)); + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForTranslations) +{ + // Test the calculateVisibleRect() function works correctly for scaling transforms. + + IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30)); + TransformationMatrix layerToSurfaceTransform; + + // Case 1: Layer is contained within the surface. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(10, 10); + IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); + + // Case 2: Layer is outside the surface rect. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(120, 120); + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_TRUE(actual.isEmpty()); + + // Case 3: Layer is partially overlapping the surface rect. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(80, 80); + expected = IntRect(IntPoint(0, 0), IntSize(20, 20)); + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor2DRotations) +{ + // Test the calculateVisibleRect() function works correctly for rotations about z-axis (i.e. 2D rotations). + // Remember that calculateVisibleRect() should return the visible rect in the layer's space. + + IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(30, 30)); + TransformationMatrix layerToSurfaceTransform; + + // Case 1: Layer is contained within the surface. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(50, 50); + layerToSurfaceTransform.rotate(45); + IntRect expected = IntRect(IntPoint(0, 0), IntSize(30, 30)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); + + // Case 2: Layer is outside the surface rect. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(-50, 0); + layerToSurfaceTransform.rotate(45); + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_TRUE(actual.isEmpty()); + + // Case 3: The layer is rotated about its top-left corner. In surface space, the layer + // is oriented diagonally, with the left half outside of the renderSurface. In + // this case, the visible rect should still be the entire layer (remember the + // visible rect is computed in layer space); both the top-left and + // bottom-right corners of the layer are still visible. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.rotate(45); + expected = IntRect(IntPoint(0, 0), IntSize(30, 30)); + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); + + // Case 4: The layer is rotated about its top-left corner, and translated upwards. In + // surface space, the layer is oriented diagonally, with only the top corner + // of the surface overlapping the layer. In layer space, the render surface + // overlaps the right side of the layer. The visible rect should be the + // layer's right half. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(0, -sqrt(2.0) * 15); + layerToSurfaceTransform.rotate(45); + expected = IntRect(IntPoint(15, 0), IntSize(15, 30)); // right half of layer bounds. + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicTransform) +{ + // Test that the calculateVisibleRect() function works correctly for 3d transforms. + + IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + TransformationMatrix layerToSurfaceTransform; + + // Case 1: Orthographic projection of a layer rotated about y-axis by 45 degrees, should be fully contained in the renderSurface. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.rotate3d(0, 45, 0); + IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); + + // Case 2: Orthographic projection of a layer rotated about y-axis by 45 degrees, but + // shifted to the side so only the right-half the layer would be visible on + // the surface. + double halfWidthOfRotatedLayer = (100.0 / sqrt(2.0)) * 0.5; // 100.0 is the un-rotated layer width; divided by sqrt(2.0) is the rotated width. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(-halfWidthOfRotatedLayer, 0); + layerToSurfaceTransform.rotate3d(0, 45, 0); // rotates about the left edge of the layer + expected = IntRect(IntPoint(50, 0), IntSize(50, 100)); // right half of the layer. + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveTransform) +{ + // Test the calculateVisibleRect() function works correctly when the layer has a + // perspective projection onto the target surface. + + IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(-50, -50), IntSize(200, 200)); + TransformationMatrix layerToSurfaceTransform; + + // Case 1: Even though the layer is twice as large as the surface, due to perspective + // foreshortening, the layer will fit fully in the surface when its translated + // more than the perspective amount. + layerToSurfaceTransform.makeIdentity(); + + // The following sequence of transforms applies the perspective about the center of the surface. + layerToSurfaceTransform.translate(50, 50); + layerToSurfaceTransform.applyPerspective(9); + layerToSurfaceTransform.translate(-50, -50); + + // This translate places the layer in front of the surface's projection plane. + layerToSurfaceTransform.translate3d(0, 0, -27); + + IntRect expected = IntRect(IntPoint(-50, -50), IntSize(200, 200)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); + + // Case 2: same projection as before, except that the layer is also translated to the + // side, so that only the right half of the layer should be visible. + // + // Explanation of expected result: + // The perspective ratio is (z distance between layer and camera origin) / (z distance between projection plane and camera origin) == ((-27 - 9) / 9) + // Then, by similar triangles, if we want to move a layer by translating -50 units in projected surface units (so that only half of it is + // visible), then we would need to translate by (-36 / 9) * -50 == -200 in the layer's units. + // + layerToSurfaceTransform.translate3d(-200, 0, 0); + expected = IntRect(IntPoint(50, -50), IntSize(100, 200)); // The right half of the layer's bounding rect. + actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dOrthographicIsNotClippedBehindSurface) +{ + // There is currently no explicit concept of an orthographic projection plane in our + // code (nor in the CSS spec to my knowledge). Therefore, layers that are technically + // behind the surface in an orthographic world should not be clipped when they are + // flattened to the surface. + + IntRect targetSurfaceRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(0, 0), IntSize(100, 100)); + TransformationMatrix layerToSurfaceTransform; + + // This sequence of transforms effectively rotates the layer about the y-axis at the + // center of the layer. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.translate(50, 0); + layerToSurfaceTransform.rotate3d(0, 45, 0); + layerToSurfaceTransform.translate(-50, 0); + + IntRect expected = IntRect(IntPoint(0, 0), IntSize(100, 100)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectFor3dPerspectiveIsClipped) +{ + // Test the calculateVisibleRect() function works correctly when projecting a surface + // onto a layer, but the layer is partially behind the camera (not just behind the + // projection plane). In this case, the cartesian coordinates may seem to be valid, + // but actually they are not. The visibleRect needs to be properly clipped by the + // w = 0 plane in homogeneous coordinates before converting to cartesian coordinates. + + IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(-10, -1), IntSize(20, 2)); + TransformationMatrix layerToSurfaceTransform; + + // The layer is positioned so that the right half of the layer should be in front of + // the camera, while the other half is behind the surface's projection plane. The + // following sequence of transforms applies the perspective and rotation about the + // center of the layer. + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.applyPerspective(1); + layerToSurfaceTransform.translate3d(0, 0, 1); + layerToSurfaceTransform.rotate3d(0, 45, 0); + + // Sanity check that this transform does indeed cause w < 0 when applying the + // transform, otherwise this code is not testing the intended scenario. + bool clipped = false; + CCMathUtil::mapQuad(layerToSurfaceTransform, FloatQuad(FloatRect(layerContentRect)), clipped); + ASSERT_TRUE(clipped); + + int expectedXPosition = -10; + int expectedWidth = 10; + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_EQ(expectedXPosition, actual.x()); + EXPECT_EQ(expectedWidth, actual.width()); +} + +TEST(CCLayerTreeHostCommonTest, verifyVisibleRectForPerspectiveUnprojection) +{ + // To determine visibleRect in layer space, there needs to be an un-projection from + // surface space to layer space. When the original transform was a perspective + // projection that was clipped, it returns a rect that encloses the clipped bounds. + // Un-projecting this new rect may require clipping again. + + // This sequence of transforms causes one corner of the layer to protrude across the w = 0 plane, and should be clipped. + IntRect targetSurfaceRect = IntRect(IntPoint(-50, -50), IntSize(100, 100)); + IntRect layerContentRect = IntRect(IntPoint(-10, -10), IntSize(20, 20)); + TransformationMatrix layerToSurfaceTransform; + layerToSurfaceTransform.makeIdentity(); + layerToSurfaceTransform.applyPerspective(1); + layerToSurfaceTransform.translate3d(0, 0, -5); + layerToSurfaceTransform.rotate3d(0, 45, 0); + layerToSurfaceTransform.rotate3d(80, 0, 0); + + // Sanity check that un-projection does indeed cause w < 0, otherwise this code is not + // testing the intended scenario. + bool clipped = false; + FloatRect clippedRect = CCMathUtil::mapClippedRect(layerToSurfaceTransform, layerContentRect); + CCMathUtil::projectQuad(layerToSurfaceTransform.inverse(), FloatQuad(clippedRect), clipped); + ASSERT_TRUE(clipped); + + // Only the corner of the layer is not visible on the surface because of being + // clipped. But, the net result of rounding visible region to an axis-aligned rect is + // that the entire layer should still be considered visible. + IntRect expected = IntRect(IntPoint(-10, -10), IntSize(20, 20)); + IntRect actual = CCLayerTreeHostCommon::calculateVisibleRect(targetSurfaceRect, layerContentRect, layerToSurfaceTransform); + EXPECT_INT_RECT_EQ(expected, actual); +} + +TEST(CCLayerTreeHostCommonTest, verifyBackFaceCulling) +{ + // Verify that layers are appropriately culled when their back face is showing and they are not double sided. + // + // Layers that are animating do not get culled on the main thread, as their transforms should be + // treated as "unknown" so we can not be sure that their back face is really showing. + // + + const TransformationMatrix identityMatrix; + RefPtr<LayerChromium> parent = LayerChromium::create(); + RefPtr<LayerChromiumWithForcedDrawsContent> child = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> animatingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> childOfAnimatingSurface = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> animatingChild = adoptRef(new LayerChromiumWithForcedDrawsContent()); + RefPtr<LayerChromiumWithForcedDrawsContent> child2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); + + parent->createRenderSurface(); + parent->addChild(child); + parent->addChild(animatingSurface); + animatingSurface->addChild(childOfAnimatingSurface); + parent->addChild(animatingChild); + parent->addChild(child2); + + // Nothing is double-sided + child->setDoubleSided(false); + child2->setDoubleSided(false); + animatingSurface->setDoubleSided(false); + childOfAnimatingSurface->setDoubleSided(false); + animatingChild->setDoubleSided(false); + + TransformationMatrix backfaceMatrix; + backfaceMatrix.translate(50, 50); + backfaceMatrix.rotate3d(0, 1, 0, 180); + backfaceMatrix.translate(-50, -50); + + // Having a descendent, and animating transforms, will make the animatingSurface own a render surface. + addAnimatedTransformToController(*animatingSurface->layerAnimationController(), 10, 30, 0); + // This is just an animating layer, not a surface. + addAnimatedTransformToController(*animatingChild->layerAnimationController(), 10, 30, 0); + + setLayerPropertiesForTesting(parent.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); + setLayerPropertiesForTesting(child.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(animatingSurface.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(childOfAnimatingSurface.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(animatingChild.get(), backfaceMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + setLayerPropertiesForTesting(child2.get(), identityMatrix, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), false); + + Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; + Vector<RefPtr<LayerChromium> > dummyLayerList; + int dummyMaxTextureSize = 512; + + parent->renderSurface()->setContentRect(IntRect(IntPoint(), parent->bounds())); + parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); + renderSurfaceLayerList.append(parent.get()); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + + EXPECT_FALSE(child->renderSurface()); + EXPECT_TRUE(animatingSurface->renderSurface()); + EXPECT_FALSE(childOfAnimatingSurface->renderSurface()); + EXPECT_FALSE(animatingChild->renderSurface()); + EXPECT_FALSE(child2->renderSurface()); + + // Verify that the animatingChild and childOfAnimatingSurface were not culled, but that child was. + ASSERT_EQ(2u, renderSurfaceLayerList.size()); + EXPECT_EQ(parent->id(), renderSurfaceLayerList[0]->id()); + EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[1]->id()); + + // The non-animating child be culled from the layer list for the parent render surface. + ASSERT_EQ(3u, renderSurfaceLayerList[0]->renderSurface()->layerList().size()); + EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[0]->id()); + EXPECT_EQ(animatingChild->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[1]->id()); + EXPECT_EQ(child2->id(), renderSurfaceLayerList[0]->renderSurface()->layerList()[2]->id()); + + ASSERT_EQ(2u, renderSurfaceLayerList[1]->renderSurface()->layerList().size()); + EXPECT_EQ(animatingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[0]->id()); + EXPECT_EQ(childOfAnimatingSurface->id(), renderSurfaceLayerList[1]->renderSurface()->layerList()[1]->id()); + + EXPECT_FALSE(child2->visibleLayerRect().isEmpty()); + + // But if the back face is visible, then the visibleLayerRect should be empty. + EXPECT_TRUE(animatingChild->visibleLayerRect().isEmpty()); + EXPECT_TRUE(animatingSurface->visibleLayerRect().isEmpty()); + // And any layers in the subtree should not be considered visible either. + EXPECT_TRUE(childOfAnimatingSurface->visibleLayerRect().isEmpty()); +} + // FIXME: // continue working on https://bugs.webkit.org/show_bug.cgi?id=68942 // - add a test to verify clipping that changes the "center point" diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp index d75c12890..ff2497085 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostImplTest.cpp @@ -26,16 +26,28 @@ #include "cc/CCLayerTreeHostImpl.h" +#include "CCAnimationTestCommon.h" +#include "CCLayerTestCommon.h" #include "FakeWebGraphicsContext3D.h" #include "GraphicsContext3DPrivate.h" #include "LayerRendererChromium.h" #include "cc/CCLayerImpl.h" +#include "cc/CCLayerTilingData.h" +#include "cc/CCQuadCuller.h" +#include "cc/CCScrollbarLayerImpl.h" #include "cc/CCSingleThreadProxy.h" -#include "cc/CCSolidColorDrawQuad.h" +#include "cc/CCTextureLayerImpl.h" +#include "cc/CCTileDrawQuad.h" +#include "cc/CCTiledLayerImpl.h" +#include "cc/CCVideoLayerImpl.h" #include <gtest/gtest.h> +#include <public/WebVideoFrame.h> +#include <public/WebVideoFrameProvider.h> +using namespace CCLayerTestCommon; using namespace WebCore; using namespace WebKit; +using namespace WebKitTests; namespace { @@ -49,11 +61,12 @@ public: m_hostImpl = CCLayerTreeHostImpl::create(settings, this); } - virtual void didLoseContextOnImplThread() { } - virtual void onSwapBuffersCompleteOnImplThread() { } - virtual void setNeedsRedrawOnImplThread() { m_didRequestRedraw = true; } - virtual void setNeedsCommitOnImplThread() { m_didRequestCommit = true; } - virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) { } + virtual void didLoseContextOnImplThread() OVERRIDE { } + virtual void onSwapBuffersCompleteOnImplThread() OVERRIDE { } + virtual void setNeedsRedrawOnImplThread() OVERRIDE { m_didRequestRedraw = true; } + virtual void setNeedsCommitOnImplThread() OVERRIDE { m_didRequestCommit = true; } + virtual void postAnimationEventsToMainThreadOnImplThread(PassOwnPtr<CCAnimationEventsVector>, double wallClockTime) OVERRIDE { } + virtual void postSetContentsMemoryAllocationLimitBytesToMainThreadOnImplThread(size_t) OVERRIDE { } static void expectClearedScrollDeltasRecursive(CCLayerImpl* layer) { @@ -250,7 +263,9 @@ TEST_F(CCLayerTreeHostImplTest, nonFastScrollableRegionWithOffset) root->setNonFastScrollableRegion(IntRect(0, 0, 50, 50)); root->setPosition(FloatPoint(-25, 0)); m_hostImpl->setRootLayer(root.release()); - m_hostImpl->drawLayers(); // Update draw transforms so we can correctly map points into layer space. + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); // Update draw transforms so we can correctly map points into layer space. // This point would fall into the non-fast scrollable region except that we've moved the layer down by 25 pixels. EXPECT_EQ(m_hostImpl->scrollBegin(IntPoint(40, 10), CCInputHandlerClient::Wheel), CCInputHandlerClient::ScrollStarted); @@ -310,7 +325,7 @@ TEST_F(CCLayerTreeHostImplTest, pinchGesture) scrollLayer->setScrollDelta(IntSize()); scrollLayer->setScrollPosition(IntPoint(50, 50)); - float pageScaleDelta = 0.1; + float pageScaleDelta = 0.1f; m_hostImpl->pinchGestureBegin(); m_hostImpl->pinchGestureUpdate(pageScaleDelta, IntPoint(0, 0)); m_hostImpl->pinchGestureEnd(); @@ -390,7 +405,7 @@ TEST_F(CCLayerTreeHostImplTest, pageScaleAnimation) } } -class DidDrawCheckLayer : public CCLayerImpl { +class DidDrawCheckLayer : public CCTiledLayerImpl { public: static PassOwnPtr<DidDrawCheckLayer> create(int id) { return adoptPtr(new DidDrawCheckLayer(id)); } @@ -407,9 +422,9 @@ public: bool didDrawCalled() const { return m_didDrawCalled; } bool willDrawCalled() const { return m_willDrawCalled; } -private: +protected: explicit DidDrawCheckLayer(int id) - : CCLayerImpl(id) + : CCTiledLayerImpl(id) , m_didDrawCalled(false) , m_willDrawCalled(false) { @@ -418,6 +433,7 @@ private: setDrawsContent(true); } +private: bool m_didDrawCalled; bool m_willDrawCalled; }; @@ -432,10 +448,14 @@ TEST_F(CCLayerTreeHostImplTest, didDrawNotCalledOnHiddenLayer) m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); + CCLayerTreeHostImpl::FrameData frame; + EXPECT_FALSE(root->willDrawCalled()); EXPECT_FALSE(root->didDrawCalled()); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); EXPECT_FALSE(root->willDrawCalled()); EXPECT_FALSE(root->didDrawCalled()); @@ -448,7 +468,9 @@ TEST_F(CCLayerTreeHostImplTest, didDrawNotCalledOnHiddenLayer) EXPECT_FALSE(root->willDrawCalled()); EXPECT_FALSE(root->didDrawCalled()); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); EXPECT_TRUE(root->willDrawCalled()); EXPECT_TRUE(root->didDrawCalled()); @@ -470,14 +492,17 @@ TEST_F(CCLayerTreeHostImplTest, didDrawCalledOnAllLayers) layer1->addChild(DidDrawCheckLayer::create(2)); DidDrawCheckLayer* layer2 = static_cast<DidDrawCheckLayer*>(layer1->children()[0].get()); - layer1->setOpacity(0.3); + layer1->setOpacity(0.3f); layer1->setPreserves3D(false); EXPECT_FALSE(root->didDrawCalled()); EXPECT_FALSE(layer1->didDrawCalled()); EXPECT_FALSE(layer2->didDrawCalled()); - m_hostImpl->drawLayers(); + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); EXPECT_TRUE(root->didDrawCalled()); EXPECT_TRUE(layer1->didDrawCalled()); @@ -487,6 +512,71 @@ TEST_F(CCLayerTreeHostImplTest, didDrawCalledOnAllLayers) EXPECT_TRUE(!!layer1->renderSurface()); } +class MissingTextureAnimatingLayer : public DidDrawCheckLayer { +public: + static PassOwnPtr<MissingTextureAnimatingLayer> create(int id, bool tileMissing, bool skipsDraw, bool animating) { return adoptPtr(new MissingTextureAnimatingLayer(id, tileMissing, skipsDraw, animating)); } + +private: + explicit MissingTextureAnimatingLayer(int id, bool tileMissing, bool skipsDraw, bool animating) + : DidDrawCheckLayer(id) + { + OwnPtr<CCLayerTilingData> tilingData = CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::NoBorderTexels); + tilingData->setBounds(bounds()); + setTilingData(*tilingData.get()); + setSkipsDraw(skipsDraw); + if (!tileMissing) + pushTileProperties(0, 0, 1, IntRect()); + if (animating) + addAnimatedTransformToLayer(*this, 10, 3, 0); + } +}; + +TEST_F(CCLayerTreeHostImplTest, prepareToDrawFailsWhenAnimationUsesCheckerboard) +{ + m_hostImpl->initializeLayerRenderer(createContext()); + m_hostImpl->setViewportSize(IntSize(10, 10)); + + // When the texture is not missing, we draw as usual. + m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); + DidDrawCheckLayer* root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); + root->addChild(MissingTextureAnimatingLayer::create(1, false, false, true)); + + CCLayerTreeHostImpl::FrameData frame; + + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + + // When a texture is missing and we're not animating, we draw as usual with checkerboarding. + m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); + root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); + root->addChild(MissingTextureAnimatingLayer::create(1, true, false, false)); + + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + + // When a texture is missing and we're animating, we don't want to draw anything. + m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); + root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); + root->addChild(MissingTextureAnimatingLayer::create(1, true, false, true)); + + m_didRequestCommit = false; + EXPECT_FALSE(m_hostImpl->prepareToDraw(frame)); + EXPECT_TRUE(m_didRequestCommit); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + + // When the layer skips draw and we're animating, we still draw the frame. + m_hostImpl->setRootLayer(DidDrawCheckLayer::create(0)); + root = static_cast<DidDrawCheckLayer*>(m_hostImpl->rootLayer()); + root->addChild(MissingTextureAnimatingLayer::create(1, false, true, true)); + + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); +} + class BlendStateTrackerContext: public FakeWebGraphicsContext3D { public: BlendStateTrackerContext() : m_blend(false) { } @@ -513,14 +603,20 @@ class BlendStateCheckLayer : public CCLayerImpl { public: static PassOwnPtr<BlendStateCheckLayer> create(int id) { return adoptPtr(new BlendStateCheckLayer(id)); } - virtual void appendQuads(CCQuadList& quadList, const CCSharedQuadState* sharedQuadState) + virtual void appendQuads(CCQuadCuller& quadList, const CCSharedQuadState* sharedQuadState, bool&) OVERRIDE { m_quadsAppended = true; - Color color = m_opaqueColor ? Color::white : Color(0, 0, 0, 0); - OwnPtr<CCDrawQuad> testBlendingDrawQuad = CCSolidColorDrawQuad::create(sharedQuadState, IntRect(5, 5, 5, 5), color); + IntRect opaqueRect; + if (opaque() || m_opaqueContents) + opaqueRect = m_quadRect; + else + opaqueRect = m_opaqueContentRect; + OwnPtr<CCDrawQuad> testBlendingDrawQuad = CCTileDrawQuad::create(sharedQuadState, m_quadRect, opaqueRect, 0, IntPoint(), IntSize(1, 1), 0, false, false, false, false, false); + testBlendingDrawQuad->setQuadVisibleRect(m_quadVisibleRect); EXPECT_EQ(m_blend, testBlendingDrawQuad->needsBlending()); EXPECT_EQ(m_hasRenderSurface, !!renderSurface()); + quadList.append(testBlendingDrawQuad.release()); } void setExpectation(bool blend, bool hasRenderSurface) @@ -532,7 +628,10 @@ public: bool quadsAppended() const { return m_quadsAppended; } - void setOpaqueColor(bool opaqueColor) { m_opaqueColor = opaqueColor; } + void setQuadRect(const IntRect& rect) { m_quadRect = rect; } + void setQuadVisibleRect(const IntRect& rect) { m_quadVisibleRect = rect; } + void setOpaqueContents(bool opaque) { m_opaqueContents = opaque; } + void setOpaqueContentRect(const IntRect& rect) { m_opaqueContentRect = rect; } private: explicit BlendStateCheckLayer(int id) @@ -540,7 +639,9 @@ private: , m_blend(false) , m_hasRenderSurface(false) , m_quadsAppended(false) - , m_opaqueColor(true) + , m_opaqueContents(false) + , m_quadRect(5, 5, 5, 5) + , m_quadVisibleRect(5, 5, 5, 5) { setAnchorPoint(FloatPoint(0, 0)); setBounds(IntSize(10, 10)); @@ -550,7 +651,10 @@ private: bool m_blend; bool m_hasRenderSurface; bool m_quadsAppended; - bool m_opaqueColor; + bool m_opaqueContents; + IntRect m_quadRect; + IntRect m_opaqueContentRect; + IntRect m_quadVisibleRect; }; // https://bugs.webkit.org/show_bug.cgi?id=75783 @@ -571,78 +675,96 @@ TEST_F(CCLayerTreeHostImplTest, blendingOffWhenDrawingOpaqueLayers) root->addChild(BlendStateCheckLayer::create(1)); BlendStateCheckLayer* layer1 = static_cast<BlendStateCheckLayer*>(root->children()[0].get()); + CCLayerTreeHostImpl::FrameData frame; + // Opaque layer, drawn without blending. layer1->setOpaque(true); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); - // Layer with translucent content, but solid color is opaque, so drawn without blending. + // Layer with translucent content, but opaque content, so drawn without blending. layer1->setOpaque(false); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Layer with translucent content and painting, so drawn with blending. layer1->setOpaque(false); - layer1->setOpaqueColor(false); + layer1->setOpaqueContents(false); layer1->setExpectation(true, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Layer with translucent opacity, drawn with blending. layer1->setOpaque(true); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setOpacity(0.5); layer1->setExpectation(true, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Layer with translucent opacity and painting, drawn with blending. layer1->setOpaque(true); - layer1->setOpaqueColor(false); + layer1->setOpaqueContents(false); layer1->setOpacity(0.5); layer1->setExpectation(true, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); layer1->addChild(BlendStateCheckLayer::create(2)); BlendStateCheckLayer* layer2 = static_cast<BlendStateCheckLayer*>(layer1->children()[0].get()); // 2 opaque layers, drawn without blending. layer1->setOpaque(true); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setOpacity(1); layer1->setExpectation(false, false); layer2->setOpaque(true); - layer2->setOpaqueColor(true); + layer2->setOpaqueContents(true); layer2->setOpacity(1); layer2->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Parent layer with translucent content, drawn with blending. // Child layer with opaque content, drawn without blending. layer1->setOpaque(false); - layer1->setOpaqueColor(false); + layer1->setOpaqueContents(false); layer1->setExpectation(true, false); layer2->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Parent layer with translucent content but opaque painting, drawn without blending. // Child layer with opaque content, drawn without blending. layer1->setOpaque(false); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setExpectation(false, false); layer2->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Parent layer with translucent opacity and opaque content. Since it has a // drawing child, it's drawn to a render surface which carries the opacity, @@ -650,53 +772,196 @@ TEST_F(CCLayerTreeHostImplTest, blendingOffWhenDrawingOpaqueLayers) // Child layer with opaque content, drawn without blending (parent surface // carries the inherited opacity). layer1->setOpaque(true); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setOpacity(0.5); layer1->setExpectation(false, true); layer2->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // Draw again, but with child non-opaque, to make sure // layer1 not culled. layer1->setOpaque(true); - layer1->setOpaqueColor(true); + layer1->setOpaqueContents(true); layer1->setOpacity(1); layer1->setExpectation(false, false); layer2->setOpaque(true); - layer2->setOpaqueColor(true); + layer2->setOpaqueContents(true); layer2->setOpacity(0.5); layer2->setExpectation(true, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // A second way of making the child non-opaque. layer1->setOpaque(true); layer1->setOpacity(1); layer1->setExpectation(false, false); layer2->setOpaque(false); - layer2->setOpaqueColor(false); + layer2->setOpaqueContents(false); layer2->setOpacity(1); layer2->setExpectation(true, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); // And when the layer says its not opaque but is painted opaque, it is not blended. layer1->setOpaque(true); layer1->setOpacity(1); layer1->setExpectation(false, false); layer2->setOpaque(false); - layer2->setOpaqueColor(true); + layer2->setOpaqueContents(true); layer2->setOpacity(1); layer2->setExpectation(false, false); - m_hostImpl->drawLayers(); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(layer1->quadsAppended()); EXPECT_TRUE(layer2->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); + + // Layer with partially opaque contents, drawn with blending. + layer1->setOpaque(false); + layer1->setQuadRect(IntRect(5, 5, 5, 5)); + layer1->setQuadVisibleRect(IntRect(5, 5, 5, 5)); + layer1->setOpaqueContents(false); + layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5)); + layer1->setExpectation(true, false); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); + + // Layer with partially opaque contents partially culled, drawn with blending. + layer1->setOpaque(false); + layer1->setQuadRect(IntRect(5, 5, 5, 5)); + layer1->setQuadVisibleRect(IntRect(5, 5, 5, 2)); + layer1->setOpaqueContents(false); + layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5)); + layer1->setExpectation(true, false); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); + + // Layer with partially opaque contents culled, drawn with blending. + layer1->setOpaque(false); + layer1->setQuadRect(IntRect(5, 5, 5, 5)); + layer1->setQuadVisibleRect(IntRect(7, 5, 3, 5)); + layer1->setOpaqueContents(false); + layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5)); + layer1->setExpectation(true, false); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); + + // Layer with partially opaque contents and translucent contents culled, drawn without blending. + layer1->setOpaque(false); + layer1->setQuadRect(IntRect(5, 5, 5, 5)); + layer1->setQuadVisibleRect(IntRect(5, 5, 2, 5)); + layer1->setOpaqueContents(false); + layer1->setOpaqueContentRect(IntRect(5, 5, 2, 5)); + layer1->setExpectation(false, false); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + EXPECT_TRUE(layer1->quadsAppended()); + m_hostImpl->didDrawAllLayers(frame); + } +TEST_F(CCLayerTreeHostImplTest, viewportCovered) +{ + m_hostImpl->initializeLayerRenderer(createContext()); + m_hostImpl->setBackgroundColor(Color::gray); + + IntSize viewportSize(1000, 1000); + m_hostImpl->setViewportSize(viewportSize); + + m_hostImpl->setRootLayer(BlendStateCheckLayer::create(0)); + BlendStateCheckLayer* root = static_cast<BlendStateCheckLayer*>(m_hostImpl->rootLayer()); + root->setExpectation(false, true); + root->setOpaque(true); + + // No gutter rects + { + IntRect layerRect(0, 0, 1000, 1000); + root->setPosition(layerRect.location()); + root->setBounds(layerRect.size()); + root->setContentBounds(layerRect.size()); + root->setQuadRect(IntRect(IntPoint(), layerRect.size())); + root->setQuadVisibleRect(IntRect(IntPoint(), layerRect.size())); + + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + ASSERT_EQ(1u, frame.renderPasses.size()); + + size_t numGutterQuads = 0; + for (size_t i = 0; i < frame.renderPasses[0]->quadList().size(); ++i) + numGutterQuads += (frame.renderPasses[0]->quadList()[i]->material() == CCDrawQuad::SolidColor) ? 1 : 0; + EXPECT_EQ(0u, numGutterQuads); + EXPECT_EQ(1u, frame.renderPasses[0]->quadList().size()); + + verifyQuadsExactlyCoverRect(frame.renderPasses[0]->quadList(), IntRect(-layerRect.location(), viewportSize)); + m_hostImpl->didDrawAllLayers(frame); + } + + // Empty visible content area (fullscreen gutter rect) + { + IntRect layerRect(0, 0, 0, 0); + root->setPosition(layerRect.location()); + root->setBounds(layerRect.size()); + root->setContentBounds(layerRect.size()); + root->setQuadRect(IntRect(IntPoint(), layerRect.size())); + root->setQuadVisibleRect(IntRect(IntPoint(), layerRect.size())); + + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + ASSERT_EQ(1u, frame.renderPasses.size()); + m_hostImpl->didDrawAllLayers(frame); + + size_t numGutterQuads = 0; + for (size_t i = 0; i < frame.renderPasses[0]->quadList().size(); ++i) + numGutterQuads += (frame.renderPasses[0]->quadList()[i]->material() == CCDrawQuad::SolidColor) ? 1 : 0; + EXPECT_EQ(1u, numGutterQuads); + EXPECT_EQ(1u, frame.renderPasses[0]->quadList().size()); + + verifyQuadsExactlyCoverRect(frame.renderPasses[0]->quadList(), IntRect(-layerRect.location(), viewportSize)); + m_hostImpl->didDrawAllLayers(frame); + } + + // Content area in middle of clip rect (four surrounding gutter rects) + { + IntRect layerRect(500, 500, 200, 200); + root->setPosition(layerRect.location()); + root->setBounds(layerRect.size()); + root->setContentBounds(layerRect.size()); + root->setQuadRect(IntRect(IntPoint(), layerRect.size())); + root->setQuadVisibleRect(IntRect(IntPoint(), layerRect.size())); + + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + ASSERT_EQ(1u, frame.renderPasses.size()); + + size_t numGutterQuads = 0; + for (size_t i = 0; i < frame.renderPasses[0]->quadList().size(); ++i) + numGutterQuads += (frame.renderPasses[0]->quadList()[i]->material() == CCDrawQuad::SolidColor) ? 1 : 0; + EXPECT_EQ(4u, numGutterQuads); + EXPECT_EQ(5u, frame.renderPasses[0]->quadList().size()); + + verifyQuadsExactlyCoverRect(frame.renderPasses[0]->quadList(), IntRect(-layerRect.location(), viewportSize)); + m_hostImpl->didDrawAllLayers(frame); + } + +} + + class ReshapeTrackerContext: public FakeWebGraphicsContext3D { public: ReshapeTrackerContext() : m_reshapeCalled(false) { } @@ -734,8 +999,11 @@ TEST_F(CCLayerTreeHostImplTest, reshapeNotCalledUntilDraw) m_hostImpl->setRootLayer(adoptPtr(root)); EXPECT_FALSE(reshapeTracker->reshapeCalled()); - m_hostImpl->drawLayers(); + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); EXPECT_TRUE(reshapeTracker->reshapeCalled()); + m_hostImpl->didDrawAllLayers(frame); } class PartialSwapTrackerContext : public FakeWebGraphicsContext3D { @@ -786,8 +1054,12 @@ TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect) root->addChild(adoptPtr(child)); layerTreeHostImpl->setRootLayer(adoptPtr(root)); + CCLayerTreeHostImpl::FrameData frame; + // First frame, the entire screen should get swapped. - layerTreeHostImpl->drawLayers(); + EXPECT_TRUE(layerTreeHostImpl->prepareToDraw(frame)); + layerTreeHostImpl->drawLayers(frame); + layerTreeHostImpl->didDrawAllLayers(frame); layerTreeHostImpl->swapBuffers(); IntRect actualSwapRect = partialSwapTracker->partialSwapRect(); IntRect expectedSwapRect = IntRect(IntPoint::zero(), IntSize(500, 500)); @@ -801,7 +1073,9 @@ TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect) // expected damage rect: IntRect(IntPoint::zero(), IntSize(26, 28)); // expected swap rect: vertically flipped, with origin at bottom left corner. child->setPosition(FloatPoint(0, 0)); - layerTreeHostImpl->drawLayers(); + EXPECT_TRUE(layerTreeHostImpl->prepareToDraw(frame)); + layerTreeHostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); layerTreeHostImpl->swapBuffers(); actualSwapRect = partialSwapTracker->partialSwapRect(); expectedSwapRect = IntRect(IntPoint(0, 500-28), IntSize(26, 28)); @@ -814,8 +1088,10 @@ TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect) // expected damage rect: IntRect(IntPoint::zero(), IntSize(500, 500)); // expected swap rect: flipped damage rect, but also clamped to viewport layerTreeHostImpl->setViewportSize(IntSize(10, 10)); - root->setOpacity(0.7); // this will damage everything - layerTreeHostImpl->drawLayers(); + root->setOpacity(0.7f); // this will damage everything + EXPECT_TRUE(layerTreeHostImpl->prepareToDraw(frame)); + layerTreeHostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); layerTreeHostImpl->swapBuffers(); actualSwapRect = partialSwapTracker->partialSwapRect(); expectedSwapRect = IntRect(IntPoint::zero(), IntSize(10, 10)); @@ -825,55 +1101,12 @@ TEST_F(CCLayerTreeHostImplTest, partialSwapReceivesDamageRect) EXPECT_EQ(expectedSwapRect.height(), actualSwapRect.height()); } -// Make sure that we reset damage tracking on visibility change because the -// state of the front buffer that we push to with PostSubBuffer is undefined. -TEST_F(CCLayerTreeHostImplTest, visibilityChangeResetsDamage) -{ - PartialSwapTrackerContext* partialSwapTracker = new PartialSwapTrackerContext(); - RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(partialSwapTracker), GraphicsContext3D::RenderDirectlyToHostWindow); - - // This test creates its own CCLayerTreeHostImpl, so - // that we can force partial swap enabled. - CCSettings settings; - settings.partialSwapEnabled = true; - OwnPtr<CCLayerTreeHostImpl> layerTreeHostImpl = CCLayerTreeHostImpl::create(settings, this); - layerTreeHostImpl->initializeLayerRenderer(context); - layerTreeHostImpl->setViewportSize(IntSize(500, 500)); - - CCLayerImpl* root = new FakeDrawableCCLayerImpl(1); - root->setAnchorPoint(FloatPoint(0, 0)); - root->setBounds(IntSize(500, 500)); - root->setDrawsContent(true); - layerTreeHostImpl->setRootLayer(adoptPtr(root)); - - // First frame: ignore. - layerTreeHostImpl->drawLayers(); - layerTreeHostImpl->swapBuffers(); - - // Second frame: nothing has changed --- so we souldn't push anything with partial swap. - layerTreeHostImpl->drawLayers(); - layerTreeHostImpl->swapBuffers(); - EXPECT_TRUE(partialSwapTracker->partialSwapRect().isEmpty()); - - // Third frame: visibility change --- so we should push a full frame with partial swap. - layerTreeHostImpl->setVisible(false); - layerTreeHostImpl->setVisible(true); - layerTreeHostImpl->drawLayers(); - layerTreeHostImpl->swapBuffers(); - IntRect actualSwapRect = partialSwapTracker->partialSwapRect(); - IntRect expectedSwapRect = IntRect(IntPoint::zero(), IntSize(500, 500)); - EXPECT_EQ(expectedSwapRect.x(), actualSwapRect.x()); - EXPECT_EQ(expectedSwapRect.y(), actualSwapRect.y()); - EXPECT_EQ(expectedSwapRect.width(), actualSwapRect.width()); - EXPECT_EQ(expectedSwapRect.height(), actualSwapRect.height()); -} - // Make sure that context lost notifications are propagated through the tree. class ContextLostNotificationCheckLayer : public CCLayerImpl { public: static PassOwnPtr<ContextLostNotificationCheckLayer> create(int id) { return adoptPtr(new ContextLostNotificationCheckLayer(id)); } - virtual void didLoseContext() + virtual void didLoseContext() OVERRIDE { m_didLoseContextCalled = true; } @@ -927,4 +1160,218 @@ TEST_F(CCLayerTreeHostImplTest, finishAllRenderingAfterContextLost) m_hostImpl->finishAllRendering(); } +class ScrollbarLayerFakePaint : public CCScrollbarLayerImpl { +public: + static PassOwnPtr<ScrollbarLayerFakePaint> create(int id) { return adoptPtr(new ScrollbarLayerFakePaint(id)); } + + virtual void paint(GraphicsContext*) { } + +private: + ScrollbarLayerFakePaint(int id) : CCScrollbarLayerImpl(id) { } +}; + +TEST_F(CCLayerTreeHostImplTest, scrollbarLayerLostContext) +{ + m_hostImpl->initializeLayerRenderer(createContext()); + m_hostImpl->setViewportSize(IntSize(10, 10)); + + m_hostImpl->setRootLayer(ScrollbarLayerFakePaint::create(0)); + ScrollbarLayerFakePaint* scrollbar = static_cast<ScrollbarLayerFakePaint*>(m_hostImpl->rootLayer()); + scrollbar->setBounds(IntSize(1, 1)); + scrollbar->setContentBounds(IntSize(1, 1)); + scrollbar->setDrawsContent(true); + + for (int i = 0; i < 2; ++i) { + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + ASSERT(frame.renderPasses.size() == 1); + CCRenderPass* renderPass = frame.renderPasses[0].get(); + // Scrollbar layer should always generate quads, even after lost context + EXPECT_GT(renderPass->quadList().size(), 0u); + m_hostImpl->didDrawAllLayers(frame); + m_hostImpl->initializeLayerRenderer(createContext()); + } +} + +// Fake WebGraphicsContext3D that will cause a failure if trying to use a +// resource that wasn't created by it (resources created by +// FakeWebGraphicsContext3D have an id of 1). +class StrictWebGraphicsContext3D : public FakeWebGraphicsContext3D { +public: + virtual WebGLId createBuffer() { return 2; } + virtual WebGLId createFramebuffer() { return 3; } + virtual WebGLId createProgram() { return 4; } + virtual WebGLId createRenderbuffer() { return 5; } + virtual WebGLId createShader(WGC3Denum) { return 6; } + virtual WebGLId createTexture() { return 7; } + + virtual void deleteBuffer(WebGLId id) + { + if (id != 2) + ADD_FAILURE() << "Trying to delete buffer id " << id; + } + + virtual void deleteFramebuffer(WebGLId id) + { + if (id != 3) + ADD_FAILURE() << "Trying to delete framebuffer id " << id; + } + + virtual void deleteProgram(WebGLId id) + { + if (id != 4) + ADD_FAILURE() << "Trying to delete program id " << id; + } + + virtual void deleteRenderbuffer(WebGLId id) + { + if (id != 5) + ADD_FAILURE() << "Trying to delete renderbuffer id " << id; + } + + virtual void deleteShader(WebGLId id) + { + if (id != 6) + ADD_FAILURE() << "Trying to delete shader id " << id; + } + + virtual void deleteTexture(WebGLId id) + { + if (id != 7) + ADD_FAILURE() << "Trying to delete texture id " << id; + } + + virtual void bindBuffer(WGC3Denum, WebGLId id) + { + if (id != 2 && id) + ADD_FAILURE() << "Trying to bind buffer id " << id; + } + + virtual void bindFramebuffer(WGC3Denum, WebGLId id) + { + if (id != 3 && id) + ADD_FAILURE() << "Trying to bind framebuffer id " << id; + } + + virtual void useProgram(WebGLId id) + { + if (id != 4) + ADD_FAILURE() << "Trying to use program id " << id; + } + + virtual void bindRenderbuffer(WGC3Denum, WebGLId id) + { + if (id != 5 && id) + ADD_FAILURE() << "Trying to bind renderbuffer id " << id; + } + + virtual void attachShader(WebGLId program, WebGLId shader) + { + if ((program != 4) || (shader != 6)) + ADD_FAILURE() << "Trying to attach shader id " << shader << " to program id " << program; + } + + virtual void bindTexture(WGC3Denum, WebGLId id) + { + if (id != 7 && id) + ADD_FAILURE() << "Trying to bind texture id " << id; + } + + static PassRefPtr<GraphicsContext3D> createGraphicsContext() + { + return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new StrictWebGraphicsContext3D()), GraphicsContext3D::RenderDirectlyToHostWindow); + } +}; + +// Fake video frame that represents a 4x4 YUV video frame. +class FakeVideoFrame: public WebVideoFrame { +public: + FakeVideoFrame() { memset(m_data, 0x80, sizeof(m_data)); } + virtual ~FakeVideoFrame() { } + virtual Format format() const { return FormatYV12; } + virtual unsigned width() const { return 4; } + virtual unsigned height() const { return 4; } + virtual unsigned planes() const { return 3; } + virtual int stride(unsigned plane) const { return 4; } + virtual const void* data(unsigned plane) const { return m_data; } + virtual unsigned textureId() const { return 0; } + virtual unsigned textureTarget() const { return 0; } + +private: + char m_data[16]; +}; + +// Fake video frame provider that always provides the same FakeVideoFrame. +class FakeVideoFrameProvider: public WebVideoFrameProvider { +public: + FakeVideoFrameProvider() : m_client(0) { } + virtual ~FakeVideoFrameProvider() + { + if (m_client) + m_client->stopUsingProvider(); + } + + virtual void setVideoFrameProviderClient(Client* client) { m_client = client; } + virtual WebVideoFrame* getCurrentFrame() { return &m_frame; } + virtual void putCurrentFrame(WebVideoFrame*) { } + +private: + FakeVideoFrame m_frame; + Client* m_client; +}; + +TEST_F(CCLayerTreeHostImplTest, dontUseOldResourcesAfterLostContext) +{ + m_hostImpl->initializeLayerRenderer(createContext()); + m_hostImpl->setViewportSize(IntSize(10, 10)); + + OwnPtr<CCLayerImpl> rootLayer(CCLayerImpl::create(0)); + rootLayer->setBounds(IntSize(10, 10)); + rootLayer->setAnchorPoint(FloatPoint(0, 0)); + + OwnPtr<CCTiledLayerImpl> tileLayer = CCTiledLayerImpl::create(1); + tileLayer->setBounds(IntSize(10, 10)); + tileLayer->setAnchorPoint(FloatPoint(0, 0)); + tileLayer->setContentBounds(IntSize(10, 10)); + tileLayer->setDrawsContent(true); + tileLayer->setSkipsDraw(false); + OwnPtr<CCLayerTilingData> tilingData(CCLayerTilingData::create(IntSize(10, 10), CCLayerTilingData::NoBorderTexels)); + tilingData->setBounds(IntSize(10, 10)); + tileLayer->setTilingData(*tilingData); + tileLayer->pushTileProperties(0, 0, 1, IntRect(0, 0, 10, 10)); + rootLayer->addChild(tileLayer.release()); + + OwnPtr<CCTextureLayerImpl> textureLayer = CCTextureLayerImpl::create(2); + textureLayer->setBounds(IntSize(10, 10)); + textureLayer->setAnchorPoint(FloatPoint(0, 0)); + textureLayer->setContentBounds(IntSize(10, 10)); + textureLayer->setDrawsContent(true); + textureLayer->setTextureId(1); + rootLayer->addChild(textureLayer.release()); + + FakeVideoFrameProvider provider; + OwnPtr<CCVideoLayerImpl> videoLayer = CCVideoLayerImpl::create(3, &provider); + videoLayer->setBounds(IntSize(10, 10)); + videoLayer->setAnchorPoint(FloatPoint(0, 0)); + videoLayer->setContentBounds(IntSize(10, 10)); + videoLayer->setDrawsContent(true); + rootLayer->addChild(videoLayer.release()); + + m_hostImpl->setRootLayer(rootLayer.release()); + + CCLayerTreeHostImpl::FrameData frame; + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + m_hostImpl->swapBuffers(); + + // Lose the context, replacing it with a StrictWebGraphicsContext3D, that + // will warn if any resource from the previous context gets used. + m_hostImpl->initializeLayerRenderer(StrictWebGraphicsContext3D::createGraphicsContext()); + EXPECT_TRUE(m_hostImpl->prepareToDraw(frame)); + m_hostImpl->drawLayers(frame); + m_hostImpl->didDrawAllLayers(frame); + m_hostImpl->swapBuffers(); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp b/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp index 2c4e7ce59..f2382b359 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp +++ b/Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp @@ -27,12 +27,13 @@ #include "cc/CCLayerTreeHost.h" #include "CCAnimationTestCommon.h" +#include "CCOcclusionTrackerTestCommon.h" +#include "CCTiledLayerTestCommon.h" #include "CompositorFakeWebGraphicsContext3D.h" #include "ContentLayerChromium.h" #include "FilterOperations.h" #include "GraphicsContext3DPrivate.h" #include "LayerChromium.h" -#include "Region.h" #include "TextureManager.h" #include "WebCompositor.h" #include "WebKit.h" @@ -44,10 +45,10 @@ #include "cc/CCScopedThreadProxy.h" #include "cc/CCTextureUpdater.h" #include "cc/CCThreadTask.h" -#include "platform/WebKitPlatformSupport.h" #include "platform/WebThread.h" #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <public/Platform.h> #include <wtf/MainThread.h> #include <wtf/PassRefPtr.h> #include <wtf/Vector.h> @@ -64,8 +65,10 @@ class TestHooks : public CCLayerAnimationDelegate { public: virtual void beginCommitOnCCThread(CCLayerTreeHostImpl*) { } virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) { } + virtual void prepareToDrawOnCCThread(CCLayerTreeHostImpl*) { } virtual void drawLayersOnCCThread(CCLayerTreeHostImpl*) { } virtual void animateLayers(CCLayerTreeHostImpl*, double monotonicTime) { } + virtual void willAnimateLayers(CCLayerTreeHostImpl*, double monotonicTime) { } virtual void applyScrollAndScale(const IntSize&, float) { } virtual void updateAnimations(double monotonicTime) { } virtual void layout() { } @@ -74,7 +77,7 @@ public: // Implementation of CCLayerAnimationDelegate virtual void notifyAnimationStarted(double time) { } - virtual void notifyAnimationFinished(int animationId) { } + virtual void notifyAnimationFinished(double time) { } }; // Adapts CCLayerTreeHostImpl for test. Runs real code, then invokes test hooks. @@ -97,15 +100,23 @@ public: m_testHooks->commitCompleteOnCCThread(this); } - virtual void drawLayers() + virtual bool prepareToDraw(FrameData& frame) { - CCLayerTreeHostImpl::drawLayers(); + bool result = CCLayerTreeHostImpl::prepareToDraw(frame); + m_testHooks->prepareToDrawOnCCThread(this); + return result; + } + + virtual void drawLayers(const FrameData& frame) + { + CCLayerTreeHostImpl::drawLayers(frame); m_testHooks->drawLayersOnCCThread(this); } protected: virtual void animateLayers(double monotonicTime, double wallClockTime) { + m_testHooks->willAnimateLayers(this, monotonicTime); CCLayerTreeHostImpl::animateLayers(monotonicTime, wallClockTime); m_testHooks->animateLayers(this, monotonicTime); } @@ -123,13 +134,13 @@ private: // Adapts CCLayerTreeHost for test. Injects MockLayerTreeHostImpl. class MockLayerTreeHost : public CCLayerTreeHost { public: - static PassRefPtr<MockLayerTreeHost> create(TestHooks* testHooks, CCLayerTreeHostClient* client, PassRefPtr<LayerChromium> rootLayer, const CCSettings& settings) + static PassOwnPtr<MockLayerTreeHost> create(TestHooks* testHooks, CCLayerTreeHostClient* client, PassRefPtr<LayerChromium> rootLayer, const CCSettings& settings) { // For these tests, we will enable threaded animations. CCSettings settingsCopy = settings; settingsCopy.threadedAnimationEnabled = true; - RefPtr<MockLayerTreeHost> layerTreeHost = adoptRef(new MockLayerTreeHost(testHooks, client, settingsCopy)); + OwnPtr<MockLayerTreeHost> layerTreeHost(adoptPtr(new MockLayerTreeHost(testHooks, client, settingsCopy))); bool success = layerTreeHost->initialize(); EXPECT_TRUE(success); layerTreeHost->setRootLayer(rootLayer); @@ -203,7 +214,7 @@ private: } Vector<WebGLId> m_textures; - HashSet<WebGLId> m_usedTextures; + HashSet<WebGLId, DefaultHash<WebGLId>::Hash, UnsignedWithZeroKeyHashTraits<WebGLId> > m_usedTextures; }; // Implementation of CCLayerTreeHost callback interface. @@ -214,22 +225,26 @@ public: return adoptPtr(new MockLayerTreeHostClient(testHooks)); } - virtual void updateAnimations(double monotonicTime) + virtual void willBeginFrame() OVERRIDE + { + } + + virtual void updateAnimations(double monotonicTime) OVERRIDE { m_testHooks->updateAnimations(monotonicTime); } - virtual void layout() + virtual void layout() OVERRIDE { m_testHooks->layout(); } - virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) + virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) OVERRIDE { m_testHooks->applyScrollAndScale(scrollDelta, scale); } - virtual PassRefPtr<GraphicsContext3D> createContext() + virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE { GraphicsContext3D::Attributes attrs; WebGraphicsContext3D::Attributes webAttrs; @@ -239,21 +254,25 @@ public: return GraphicsContext3DPrivate::createGraphicsContextFromWebContext(webContext.release(), GraphicsContext3D::RenderDirectlyToHostWindow); } - virtual void didCommitAndDrawFrame() + virtual void didCommit() OVERRIDE + { + } + + virtual void didCommitAndDrawFrame() OVERRIDE { m_testHooks->didCommitAndDrawFrame(); } - virtual void didCompleteSwapBuffers() + virtual void didCompleteSwapBuffers() OVERRIDE { } - virtual void didRecreateContext(bool succeeded) + virtual void didRecreateContext(bool succeeded) OVERRIDE { m_testHooks->didRecreateContext(succeeded); } - virtual void scheduleComposite() + virtual void scheduleComposite() OVERRIDE { } @@ -300,6 +319,11 @@ public: callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsCommit, this); } + void AcquireLayerTextures() + { + callOnMainThread(CCLayerTreeHostTest::dispatchAcquireLayerTextures, this); + } + void postSetNeedsRedrawToMainThread() { callOnMainThread(CCLayerTreeHostTest::dispatchSetNeedsRedraw, this); @@ -340,7 +364,7 @@ protected: static void onEndTest(void* self) { ASSERT(isMainThread()); - webKitPlatformSupport()->currentThread()->exitRunLoop(); + WebKit::Platform::current()->currentThread()->exitRunLoop(); } static void dispatchSetNeedsAnimate(void* self) @@ -358,7 +382,7 @@ protected: CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); ASSERT(test); if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) - addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 0, 0, 1); + addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 0, 0, 0.5, false); } static void dispatchAddAnimation(void* self) @@ -367,7 +391,7 @@ protected: CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); ASSERT(test); if (test->m_layerTreeHost && test->m_layerTreeHost->rootLayer()) - addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 10, 0, 1); + addOpacityTransitionToLayer(*test->m_layerTreeHost->rootLayer(), 10, 0, 0.5, true); } static void dispatchSetNeedsAnimateAndCommit(void* self) @@ -390,6 +414,15 @@ protected: test->m_layerTreeHost->setNeedsCommit(); } + static void dispatchAcquireLayerTextures(void* self) + { + ASSERT(isMainThread()); + CCLayerTreeHostTest* test = static_cast<CCLayerTreeHostTest*>(self); + ASSERT_TRUE(test); + if (test->m_layerTreeHost) + test->m_layerTreeHost->acquireLayerTextures(); + } + static void dispatchSetNeedsRedraw(void* self) { ASSERT(isMainThread()); @@ -464,7 +497,7 @@ protected: virtual void runTest(bool threaded) { if (threaded) { - m_webThread = adoptPtr(webKitPlatformSupport()->createThread("CCLayerTreeHostTest")); + m_webThread = adoptPtr(WebKit::Platform::current()->createThread("CCLayerTreeHostTest")); WebCompositor::initialize(m_webThread.get()); } else WebCompositor::initialize(0); @@ -473,10 +506,10 @@ protected: m_mainThreadProxy = CCScopedThreadProxy::create(CCProxy::mainThread()); m_beginTask = new BeginTask(this); - webKitPlatformSupport()->currentThread()->postDelayedTask(m_beginTask, 0); // postDelayedTask takes ownership of the task + WebKit::Platform::current()->currentThread()->postDelayedTask(m_beginTask, 0); // postDelayedTask takes ownership of the task m_timeoutTask = new TimeoutTask(this); - webKitPlatformSupport()->currentThread()->postDelayedTask(m_timeoutTask, 5000); - webKitPlatformSupport()->currentThread()->enterRunLoop(); + WebKit::Platform::current()->currentThread()->postDelayedTask(m_timeoutTask, 5000); + WebKit::Platform::current()->currentThread()->enterRunLoop(); if (m_layerTreeHost && m_layerTreeHost->rootLayer()) m_layerTreeHost->rootLayer()->setLayerTreeHost(0); @@ -498,7 +531,7 @@ protected: CCSettings m_settings; OwnPtr<MockLayerTreeHostClient> m_client; - RefPtr<CCLayerTreeHost> m_layerTreeHost; + OwnPtr<CCLayerTreeHost> m_layerTreeHost; private: bool m_beginning; @@ -520,6 +553,7 @@ void CCLayerTreeHostTest::doBeginTest() m_layerTreeHost = MockLayerTreeHost::create(this, m_client.get(), rootLayer, m_settings); ASSERT_TRUE(m_layerTreeHost); rootLayer->setLayerTreeHost(m_layerTreeHost.get()); + m_layerTreeHost->setSurfaceReady(); m_beginning = true; beginTest(); @@ -806,6 +840,134 @@ TEST_F(CCLayerTreeHostTestSetNeedsRedraw, runMultiThread) runTestThreaded(); } + +// beginLayerWrite should prevent draws from executing until a commit occurs +class CCLayerTreeHostTestWriteLayersRedraw : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestWriteLayersRedraw() + : m_numCommits(0) + , m_numDraws(0) + { + } + + virtual void beginTest() + { + AcquireLayerTextures(); + postSetNeedsRedrawToMainThread(); // should be inhibited without blocking + postSetNeedsCommitToMainThread(); + } + + virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) + { + EXPECT_EQ(1, impl->sourceFrameNumber()); + m_numDraws++; + } + + virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) + { + m_numCommits++; + endTest(); + } + + virtual void afterTest() + { + EXPECT_EQ(0, m_numDraws); + EXPECT_EQ(1, m_numCommits); + } + +private: + int m_numCommits; + int m_numDraws; +}; + +TEST_F(CCLayerTreeHostTestWriteLayersRedraw, runMultiThread) +{ + runTestThreaded(); +} + +// Verify that when resuming visibility, requesting layer write permission +// will not deadlock the main thread even though there are not yet any +// scheduled redraws. This behavior is critical for reliably surviving tab +// switching. There are no failure conditions to this test, it just passes +// by not timing out. +class CCLayerTreeHostTestWriteLayersAfterVisible : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestWriteLayersAfterVisible() + : m_numCommits(0) + { + } + + virtual void beginTest() + { + postSetNeedsCommitToMainThread(); + } + + virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) + { + m_numCommits++; + if (m_numCommits == 2) + endTest(); + else { + postSetVisibleToMainThread(false); + postSetVisibleToMainThread(true); + AcquireLayerTextures(); + postSetNeedsCommitToMainThread(); + } + } + + virtual void afterTest() + { + } + +private: + int m_numCommits; +}; + +TEST_F(CCLayerTreeHostTestWriteLayersAfterVisible, runMultiThread) +{ + runTestThreaded(); +} + +// A compositeAndReadback while invisible should force a normal commit without assertion. +class CCLayerTreeHostTestCompositeAndReadbackWhileInvisible : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestCompositeAndReadbackWhileInvisible() + : m_numCommits(0) + { + } + + virtual void beginTest() + { + } + + virtual void didCommitAndDrawFrame() + { + m_numCommits++; + if (m_numCommits == 1) { + m_layerTreeHost->setVisible(false); + m_layerTreeHost->setNeedsCommit(); + m_layerTreeHost->setNeedsCommit(); + OwnArrayPtr<char> pixels(adoptArrayPtr(new char[4])); + m_layerTreeHost->compositeAndReadback(static_cast<void*>(pixels.get()), IntRect(0, 0, 1, 1)); + } else + endTest(); + + } + + virtual void afterTest() + { + } + +private: + int m_numCommits; +}; + +TEST_F(CCLayerTreeHostTestCompositeAndReadbackWhileInvisible, runMultiThread) +{ + runTestThreaded(); +} + + // Trigger a frame with setNeedsCommit. Then, inside the resulting animate // callback, requet another frame using setNeedsAnimate. End the test when // animate gets called yet-again, indicating that the proxy is correctly @@ -853,7 +1015,6 @@ public: CCLayerTreeHostTestAddAnimation() : m_numAnimates(0) , m_receivedAnimationStartedNotification(false) - , m_receivedAnimationFinishedNotification(false) , m_startTime(0) , m_firstMonotonicTime(0) { @@ -878,7 +1039,6 @@ public: EXPECT_LT(0, m_firstMonotonicTime); EXPECT_NE(m_startTime, m_firstMonotonicTime); EXPECT_TRUE(m_receivedAnimationStartedNotification); - EXPECT_TRUE(m_receivedAnimationFinishedNotification); endTest(); } @@ -888,11 +1048,6 @@ public: m_startTime = wallClockTime; } - virtual void notifyAnimationFinished(int) - { - m_receivedAnimationFinishedNotification = true; - } - virtual void afterTest() { } @@ -900,7 +1055,6 @@ public: private: int m_numAnimates; bool m_receivedAnimationStartedNotification; - bool m_receivedAnimationFinishedNotification; double m_startTime; double m_firstMonotonicTime; }; @@ -947,6 +1101,153 @@ TEST_F(CCLayerTreeHostTestTickAnimationWhileBackgrounded, runMultiThread) runTestThreaded(); } +// Ensures that animations continue to be ticked when we are backgrounded. +class CCLayerTreeHostTestAddAnimationWithTimingFunction : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestAddAnimationWithTimingFunction() + { + } + + virtual void beginTest() + { + postAddAnimationToMainThread(); + } + + virtual void animateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) + { + const CCFloatAnimationCurve* curve = m_layerTreeHost->rootLayer()->layerAnimationController()->getActiveAnimation(0, CCActiveAnimation::Opacity)->curve()->toFloatAnimationCurve(); + float startOpacity = curve->getValue(0); + float endOpacity = curve->getValue(curve->duration()); + float linearlyInterpolatedOpacity = 0.25 * endOpacity + 0.75 * startOpacity; + double time = curve->duration() * 0.25; + // If the linear timing function associated with this animation was not picked up, + // then the linearly interpolated opacity would be different because of the + // default ease timing function. + EXPECT_FLOAT_EQ(linearlyInterpolatedOpacity, curve->getValue(time)); + endTest(); + } + + virtual void afterTest() + { + } + +private: +}; + +TEST_F(CCLayerTreeHostTestAddAnimationWithTimingFunction, runMultiThread) +{ + runTestThreaded(); +} + +// Ensures that when opacity is being animated, this value does not cause the subtree to be skipped. +class CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity() + { + } + + virtual void beginTest() + { + m_layerTreeHost->rootLayer()->setDrawOpacity(1); + m_layerTreeHost->setViewportSize(IntSize(10, 10)); + m_layerTreeHost->rootLayer()->setOpacity(0); + postAddAnimationToMainThread(); + } + + virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl*) + { + // If the subtree was skipped when preparing to draw, the layer's draw opacity + // will not have been updated. It should be set to 0 due to the animation. + // Without the animation, the layer will be skipped since it has zero opacity. + EXPECT_EQ(0, m_layerTreeHost->rootLayer()->drawOpacity()); + endTest(); + } + + virtual void afterTest() + { + } +}; + +TEST_F(CCLayerTreeHostTestDoNotSkipLayersWithAnimatedOpacity, runMultiThread) +{ + runTestThreaded(); +} + +// Ensures that main thread animations have their start times synchronized with impl thread animations. +class CCLayerTreeHostTestSynchronizeAnimationStartTimes : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestSynchronizeAnimationStartTimes() + : m_layerTreeHostImpl(0) + { + } + + virtual void beginTest() + { + postAddAnimationToMainThread(); + } + + // This is guaranteed to be called before CCLayerTreeHostImpl::animateLayers. + virtual void willAnimateLayers(CCLayerTreeHostImpl* layerTreeHostImpl, double monotonicTime) + { + m_layerTreeHostImpl = layerTreeHostImpl; + } + + virtual void notifyAnimationStarted(double time) + { + EXPECT_TRUE(m_layerTreeHostImpl); + + CCLayerAnimationController* controllerImpl = m_layerTreeHostImpl->rootLayer()->layerAnimationController(); + CCLayerAnimationController* controller = m_layerTreeHost->rootLayer()->layerAnimationController(); + CCActiveAnimation* animationImpl = controllerImpl->getActiveAnimation(0, CCActiveAnimation::Opacity); + CCActiveAnimation* animation = controller->getActiveAnimation(0, CCActiveAnimation::Opacity); + + EXPECT_EQ(animationImpl->startTime(), animation->startTime()); + + endTest(); + } + + virtual void afterTest() + { + } + +private: + CCLayerTreeHostImpl* m_layerTreeHostImpl; +}; + +TEST_F(CCLayerTreeHostTestSynchronizeAnimationStartTimes, runMultiThread) +{ + runTestThreaded(); +} + +// Ensures that main thread animations have their start times synchronized with impl thread animations. +class CCLayerTreeHostTestAnimationFinishedEvents : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestAnimationFinishedEvents() + { + } + + virtual void beginTest() + { + postAddInstantAnimationToMainThread(); + } + + virtual void notifyAnimationFinished(double time) + { + endTest(); + } + + virtual void afterTest() + { + } + +private: +}; + +TEST_F(CCLayerTreeHostTestAnimationFinishedEvents, runMultiThread) +{ + runTestThreaded(); +} + class CCLayerTreeHostTestScrollSimple : public CCLayerTreeHostTestThreadOnly { public: CCLayerTreeHostTestScrollSimple() @@ -1096,6 +1397,38 @@ TEST_F(CCLayerTreeHostTestScrollMultipleRedraw, DISABLED_runMultiThread) runTestThreaded(); } +// This test verifies that properties on the layer tree host are commited to the impl side. +class CCLayerTreeHostTestCommit : public CCLayerTreeHostTest { +public: + + CCLayerTreeHostTestCommit() { } + + virtual void beginTest() + { + m_layerTreeHost->setViewportSize(IntSize(20, 20)); + m_layerTreeHost->setBackgroundColor(Color::gray); + m_layerTreeHost->setPageScaleFactorAndLimits(5, 5, 5); + + postSetNeedsCommitToMainThread(); + } + + virtual void commitCompleteOnCCThread(CCLayerTreeHostImpl* impl) + { + EXPECT_EQ(IntSize(20, 20), impl->viewportSize()); + EXPECT_EQ(Color::gray, impl->backgroundColor()); + EXPECT_EQ(5, impl->pageScale()); + + endTest(); + } + + virtual void afterTest() { } +}; + +TEST_F(CCLayerTreeHostTestCommit, runTest) +{ + runTest(true); +} + // Verifies that startPageScaleAnimation events propagate correctly from CCLayerTreeHost to // CCLayerTreeHostImpl in the MT compositor. class CCLayerTreeHostTestStartPageScaleAnimation : public CCLayerTreeHostTest { @@ -1230,33 +1563,23 @@ public: int idlePaintContentsCount() { return m_idlePaintContentsCount; } void resetPaintContentsCount() { m_paintContentsCount = 0; m_idlePaintContentsCount = 0;} - int updateCount() { return m_updateCount; } - void resetUpdateCount() { m_updateCount = 0; } - - virtual void paintContentsIfDirty(const Region& occludedScreenSpace) + virtual void update(CCTextureUpdater& updater, const CCOcclusionTracker* occlusion) OVERRIDE { - ContentLayerChromium::paintContentsIfDirty(occludedScreenSpace); + ContentLayerChromium::update(updater, occlusion); m_paintContentsCount++; } - virtual void idlePaintContentsIfDirty(const Region& occluded) + virtual void idleUpdate(CCTextureUpdater& updater, const CCOcclusionTracker* occlusion) OVERRIDE { - ContentLayerChromium::idlePaintContentsIfDirty(occluded); + ContentLayerChromium::idleUpdate(updater, occlusion); m_idlePaintContentsCount++; } - virtual void updateCompositorResources(GraphicsContext3D* context, CCTextureUpdater& updater) - { - ContentLayerChromium::updateCompositorResources(context, updater); - m_updateCount++; - } - private: explicit ContentLayerChromiumWithUpdateTracking(ContentLayerDelegate* delegate) : ContentLayerChromium(delegate) , m_paintContentsCount(0) , m_idlePaintContentsCount(0) - , m_updateCount(0) { setBounds(IntSize(10, 10)); setIsDrawable(true); @@ -1264,7 +1587,6 @@ private: int m_paintContentsCount; int m_idlePaintContentsCount; - int m_updateCount; }; // Layer opacity change during paint should not prevent compositor resources from being updated during commit. @@ -1291,17 +1613,12 @@ public: virtual void afterTest() { - // paintContentsIfDirty() should have been called once. + // update() should have been called once. EXPECT_EQ(1, m_updateCheckLayer->paintContentsCount()); - // idlePaintContentsIfDirty() should have been called once + // idleUpdate() should have been called once EXPECT_EQ(1, m_updateCheckLayer->idlePaintContentsCount()); - // updateCompositorResources() should have been called the same - // amout of times as paintContentsIfDirty(). - EXPECT_EQ(m_updateCheckLayer->paintContentsCount(), - m_updateCheckLayer->updateCount()); - // clear m_updateCheckLayer so CCLayerTreeHost dies. m_updateCheckLayer.clear(); } @@ -1330,7 +1647,8 @@ public: IntSize viewportSize(10, 10); layerTreeHost()->setViewportSize(viewportSize); - layerTreeHost()->updateLayers(); + CCTextureUpdater updater; + layerTreeHost()->updateLayers(updater); EXPECT_EQ(viewportSize, layerTreeHost()->viewportSize()); EXPECT_EQ(TextureManager::highLimitBytes(viewportSize), layerTreeHost()->contentsTextureManager()->maxMemoryLimitBytes()); @@ -1457,6 +1775,18 @@ TEST_F(CCLayerTreeHostTestAtomicCommit, runMultiThread) runTest(true); } +static void setLayerPropertiesForTesting(LayerChromium* layer, LayerChromium* parent, const TransformationMatrix& transform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool opaque) +{ + layer->removeAllChildren(); + if (parent) + parent->addChild(layer); + layer->setTransform(transform); + layer->setAnchorPoint(anchor); + layer->setPosition(position); + layer->setBounds(bounds); + layer->setOpaque(opaque); +} + class CCLayerTreeHostTestAtomicCommitWithPartialUpdate : public CCLayerTreeHostTest { public: CCLayerTreeHostTestAtomicCommitWithPartialUpdate() @@ -1471,10 +1801,11 @@ public: virtual void beginTest() { m_layerTreeHost->setRootLayer(m_parent); - m_layerTreeHost->setViewportSize(IntSize(10, 10)); - m_parent->addChild(m_child); - m_child->setOpacity(0.5); - m_child->setBounds(IntSize(20, 20)); + m_layerTreeHost->setViewportSize(IntSize(10, 20)); + + TransformationMatrix identityMatrix; + setLayerPropertiesForTesting(m_parent.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(10, 20), true); + setLayerPropertiesForTesting(m_child.get(), m_parent.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(0, 10), IntSize(10, 10), false); postSetNeedsCommitToMainThread(); postSetNeedsRedrawToMainThread(); @@ -1518,15 +1849,31 @@ public: EXPECT_EQ(3, context->numTextures()); // Number of textures used for commit should still be two. EXPECT_EQ(2, context->numUsedTextures()); - // First texture should not have been used. - EXPECT_FALSE(context->usedTexture(context->texture(0))); - // Second texture should have been used. - EXPECT_TRUE(context->usedTexture(context->texture(1))); - // New textures should have been used. + // First texture should have been used. + EXPECT_TRUE(context->usedTexture(context->texture(0))); + // Second texture should not have been used. + EXPECT_FALSE(context->usedTexture(context->texture(1))); + // Third texture should have been used. EXPECT_TRUE(context->usedTexture(context->texture(2))); context->resetUsedTextures(); break; + case 3: + // Number of textures should be two. + EXPECT_EQ(2, context->numTextures()); + // No textures should be used for commit. + EXPECT_EQ(0, context->numUsedTextures()); + + context->resetUsedTextures(); + break; + case 4: + // Number of textures should be two. + EXPECT_EQ(2, context->numTextures()); + // Number of textures used for commit should be one. + EXPECT_EQ(1, context->numUsedTextures()); + + context->resetUsedTextures(); + break; default: ASSERT_NOT_REACHED(); break; @@ -1537,10 +1884,14 @@ public: { CompositorFakeWebGraphicsContext3DWithTextureTracking* context = static_cast<CompositorFakeWebGraphicsContext3DWithTextureTracking*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(impl->context())); - // Number of textures used for drawing should always be two. - EXPECT_EQ(2, context->numUsedTextures()); + // Number of textures used for drawing should two except for frame 4 + // where the viewport only contains one layer. + if (impl->frameNumber() == 4) + EXPECT_EQ(1, context->numUsedTextures()); + else + EXPECT_EQ(2, context->numUsedTextures()); - if (impl->frameNumber() < 3) { + if (impl->frameNumber() < 5) { context->resetUsedTextures(); postSetNeedsAnimateAndCommitToMainThread(); postSetNeedsRedrawToMainThread(); @@ -1561,6 +1912,13 @@ public: m_parent->setNeedsDisplayRect(FloatRect(0, 0, 5, 5)); m_child->setNeedsDisplayRect(FloatRect(0, 0, 5, 5)); break; + case 3: + m_child->setNeedsDisplay(); + m_layerTreeHost->setViewportSize(IntSize(10, 10)); + break; + case 4: + m_layerTreeHost->setViewportSize(IntSize(10, 20)); + break; default: ASSERT_NOT_REACHED(); break; @@ -1593,12 +1951,14 @@ class TestLayerChromium : public LayerChromium { public: static PassRefPtr<TestLayerChromium> create() { return adoptRef(new TestLayerChromium()); } - virtual void paintContentsIfDirty(const Region& occludedScreenSpace) + virtual void update(CCTextureUpdater&, const CCOcclusionTracker* occlusion) OVERRIDE { - m_occludedScreenSpace = occludedScreenSpace; + // Gain access to internals of the CCOcclusionTracker. + const TestCCOcclusionTracker* testOcclusion = static_cast<const TestCCOcclusionTracker*>(occlusion); + m_occludedScreenSpace = testOcclusion ? testOcclusion->occlusionInScreenSpace() : Region(); } - virtual bool drawsContent() const { return true; } + virtual bool drawsContent() const OVERRIDE { return true; } const Region& occludedScreenSpace() const { return m_occludedScreenSpace; } void clearOccludedScreenSpace() { m_occludedScreenSpace = Region(); } @@ -1609,16 +1969,9 @@ private: Region m_occludedScreenSpace; }; -static void setLayerPropertiesForTesting(TestLayerChromium* layer, LayerChromium* parent, const TransformationMatrix& transform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool opaque) +static void setTestLayerPropertiesForTesting(TestLayerChromium* layer, LayerChromium* parent, const TransformationMatrix& transform, const FloatPoint& anchor, const FloatPoint& position, const IntSize& bounds, bool opaque) { - layer->removeAllChildren(); - if (parent) - parent->addChild(layer); - layer->setTransform(transform); - layer->setAnchorPoint(anchor); - layer->setPosition(position); - layer->setBounds(bounds); - layer->setOpaque(opaque); + setLayerPropertiesForTesting(layer, parent, transform, anchor, position, bounds, opaque); layer->clearOccludedScreenSpace(); } @@ -1646,81 +1999,82 @@ public: // positioned on the screen. // The child layer is rotated and the grandChild is opaque, but clipped to the child and rootLayer - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), false); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + CCTextureUpdater updater; + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // If the child layer is opaque, then it adds to the occlusion seen by the rootLayer. - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); + setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 30, 170, 170), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // Add a second child to the root layer and the regions should merge - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(70, 20), IntSize(500, 500), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(70, 20), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), child2->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 30, 170, 170), child2->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 20, 70, 80), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 20, 170, 180), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(2u, rootLayer->occludedScreenSpace().rects().size()); // Move the second child to be sure. - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 30, 70, 70), child2->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 30, 170, 170), child2->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 30, 90, 70), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 30, 190, 170), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(2u, rootLayer->occludedScreenSpace().rects().size()); // If the child layer has a mask on it, then it shouldn't contribute to occlusion on stuff below it - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); + setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); @@ -1729,20 +2083,20 @@ public: m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds()); EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // If the child layer with a mask is below child2, then child2 should contribute to occlusion on everything, and child shouldn't contribute to the rootLayer - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); + setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); @@ -1751,62 +2105,62 @@ public: m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds()); EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), grandChild->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(1u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 40, 90, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 40, 190, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(2u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // If the child layer has a non-opaque drawOpacity, then it shouldn't contribute to occlusion on stuff below it - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); child->setMaskLayer(0); child->setOpacity(0.5); m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds()); EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // If the child layer with non-opaque drawOpacity is below child2, then child2 should contribute to occlusion on everything, and child shouldn't contribute to the rootLayer - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); - setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); child->setMaskLayer(0); child->setOpacity(0.5); m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds()); EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), grandChild->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(1u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 40, 90, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 40, 190, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(2u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // Kill the layerTreeHost immediately. @@ -1845,10 +2199,10 @@ public: // If the child layer has a filter that changes alpha values, and is below child2, then child2 should contribute to occlusion on everything, // and child shouldn't contribute to the rootLayer - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); - setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); { FilterOperations filters; @@ -1858,24 +2212,25 @@ public: m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + CCTextureUpdater updater; + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds()); EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), grandChild->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(1u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 40, 90, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 40, 190, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(2u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // If the child layer has a filter that moves pixels/changes alpha, and is below child2, then child should not inherit occlusion from outside its subtree, // and should not contribute to the rootLayer - setLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); - setLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); - setLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); - setLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(rootLayer.get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); + setTestLayerPropertiesForTesting(child.get(), rootLayer.get(), childTransform, FloatPoint(0, 0), FloatPoint(30, 30), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(grandChild.get(), child.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 10), IntSize(500, 500), true); + setTestLayerPropertiesForTesting(child2.get(), rootLayer.get(), identityMatrix, FloatPoint(0, 0), FloatPoint(10, 70), IntSize(500, 500), true); { FilterOperations filters; @@ -1885,22 +2240,23 @@ public: m_layerTreeHost->setRootLayer(rootLayer); m_layerTreeHost->setViewportSize(rootLayer->bounds()); - m_layerTreeHost->updateLayers(); + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); EXPECT_EQ_RECT(IntRect(), child2->occludedScreenSpace().bounds()); EXPECT_EQ(0u, child2->occludedScreenSpace().rects().size()); EXPECT_EQ_RECT(IntRect(), grandChild->occludedScreenSpace().bounds()); EXPECT_EQ(0u, grandChild->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(30, 40, 70, 60), child->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(30, 40, 170, 160), child->occludedScreenSpace().bounds()); EXPECT_EQ(1u, child->occludedScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 70, 90, 30), rootLayer->occludedScreenSpace().bounds()); + EXPECT_EQ_RECT(IntRect(10, 70, 190, 130), rootLayer->occludedScreenSpace().bounds()); EXPECT_EQ(1u, rootLayer->occludedScreenSpace().rects().size()); // Kill the layerTreeHost immediately. m_layerTreeHost->setRootLayer(0); m_layerTreeHost.clear(); + CCLayerTreeHost::setNeedsFilterContext(false); endTest(); } @@ -1928,10 +2284,10 @@ public: for (int i = 0; i < numSurfaces; ++i) { layers.append(TestLayerChromium::create()); if (!i) { - setLayerPropertiesForTesting(layers.last().get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(100, 100), true); + setTestLayerPropertiesForTesting(layers.last().get(), 0, identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(200, 200), true); layers.last()->createRenderSurface(); } else { - setLayerPropertiesForTesting(layers.last().get(), layers[layers.size()-2].get(), identityMatrix, FloatPoint(0, 0), FloatPoint(1, 1), IntSize(100-i, 100-i), true); + setTestLayerPropertiesForTesting(layers.last().get(), layers[layers.size()-2].get(), identityMatrix, FloatPoint(0, 0), FloatPoint(1, 1), IntSize(200-i, 200-i), true); layers.last()->setMasksToBounds(true); layers.last()->setReplicaLayer(replica.get()); // Make it have a RenderSurface } @@ -1939,16 +2295,17 @@ public: for (int i = 1; i < numSurfaces; ++i) { children.append(TestLayerChromium::create()); - setLayerPropertiesForTesting(children.last().get(), layers[i].get(), identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false); + setTestLayerPropertiesForTesting(children.last().get(), layers[i].get(), identityMatrix, FloatPoint(0, 0), FloatPoint(0, 0), IntSize(500, 500), false); } m_layerTreeHost->setRootLayer(layers[0].get()); m_layerTreeHost->setViewportSize(layers[0]->bounds()); - m_layerTreeHost->updateLayers(); + CCTextureUpdater updater; + m_layerTreeHost->updateLayers(updater); m_layerTreeHost->commitComplete(); for (int i = 0; i < numSurfaces-1; ++i) { - IntRect expectedOcclusion(i+1, i+1, 100-i-1, 100-i-1); + IntRect expectedOcclusion(i+1, i+1, 200-i-1, 200-i-1); EXPECT_EQ_RECT(expectedOcclusion, layers[i]->occludedScreenSpace().bounds()); EXPECT_EQ(1u, layers[i]->occludedScreenSpace().rects().size()); @@ -2036,4 +2393,57 @@ TEST_F(CCLayerTreeHostTestSetRepeatedLostContext, runMultiThread) runTestThreaded(); } +class CCLayerTreeHostTestFractionalScroll : public CCLayerTreeHostTestThreadOnly { +public: + CCLayerTreeHostTestFractionalScroll() + : m_scrollAmount(1.75, 0) + { + } + + virtual void beginTest() + { + m_layerTreeHost->rootLayer()->setScrollable(true); + postSetNeedsCommitToMainThread(); + } + + virtual void drawLayersOnCCThread(CCLayerTreeHostImpl* impl) + { + CCLayerImpl* root = impl->rootLayer(); + root->setMaxScrollPosition(IntSize(100, 100)); + + // Check that a fractional scroll delta is correctly accumulated over multiple commits. + if (impl->frameNumber() == 1) { + EXPECT_EQ(root->scrollPosition(), IntPoint(0, 0)); + EXPECT_EQ(root->scrollDelta(), FloatSize(0, 0)); + postSetNeedsCommitToMainThread(); + } else if (impl->frameNumber() == 2) { + EXPECT_EQ(root->scrollPosition(), flooredIntPoint(m_scrollAmount)); + EXPECT_EQ(root->scrollDelta(), FloatSize(fmod(m_scrollAmount.width(), 1), 0)); + postSetNeedsCommitToMainThread(); + } else if (impl->frameNumber() == 3) { + EXPECT_EQ(root->scrollPosition(), flooredIntPoint(m_scrollAmount + m_scrollAmount)); + EXPECT_EQ(root->scrollDelta(), FloatSize(fmod(2 * m_scrollAmount.width(), 1), 0)); + endTest(); + } + root->scrollBy(m_scrollAmount); + } + + virtual void applyScrollAndScale(const IntSize& scrollDelta, float scale) + { + IntPoint position = m_layerTreeHost->rootLayer()->scrollPosition(); + m_layerTreeHost->rootLayer()->setScrollPosition(position + scrollDelta); + } + + virtual void afterTest() + { + } +private: + FloatSize m_scrollAmount; +}; + +TEST_F(CCLayerTreeHostTestFractionalScroll, runMultiThread) +{ + runTestThreaded(); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCLayerTreeTestCommon.h b/Source/WebKit/chromium/tests/CCLayerTreeTestCommon.h index 0b83dacab..75e487a4a 100644 --- a/Source/WebKit/chromium/tests/CCLayerTreeTestCommon.h +++ b/Source/WebKit/chromium/tests/CCLayerTreeTestCommon.h @@ -27,13 +27,19 @@ namespace WebKitTests { -// This is a macro instead of function so that we get useful line numbers where a test failed. +// These are macros instead of functions so that we get useful line numbers where a test failed. #define EXPECT_FLOAT_RECT_EQ(expected, actual) \ EXPECT_FLOAT_EQ((expected).location().x(), (actual).location().x()); \ EXPECT_FLOAT_EQ((expected).location().y(), (actual).location().y()); \ EXPECT_FLOAT_EQ((expected).size().width(), (actual).size().width()); \ EXPECT_FLOAT_EQ((expected).size().height(), (actual).size().height()) +#define EXPECT_INT_RECT_EQ(expected, actual) \ + EXPECT_EQ((expected).location().x(), (actual).location().x()); \ + EXPECT_EQ((expected).location().y(), (actual).location().y()); \ + EXPECT_EQ((expected).size().width(), (actual).size().width()); \ + EXPECT_EQ((expected).size().height(), (actual).size().height()) + // This is a macro instead of a function so that we get useful line numbers where a test failed. // Even though TransformationMatrix values are double precision, there are many other floating-point values used that affect // the transforms, and so we only expect them to be accurate up to floating-point precision. diff --git a/Source/WebKit/chromium/tests/CCMathUtilTest.cpp b/Source/WebKit/chromium/tests/CCMathUtilTest.cpp new file mode 100644 index 000000000..db0a4e53f --- /dev/null +++ b/Source/WebKit/chromium/tests/CCMathUtilTest.cpp @@ -0,0 +1,102 @@ +/* + * 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/CCMathUtil.h" + +#include "TransformationMatrix.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +using namespace WebCore; + +namespace { + +TEST(CCMathUtilTest, verifyBackfaceVisibilityBasicCases) +{ + TransformationMatrix transform; + + transform.makeIdentity(); + EXPECT_FALSE(transform.isBackFaceVisible()); + + transform.makeIdentity(); + transform.rotate3d(0, 80, 0); + EXPECT_FALSE(transform.isBackFaceVisible()); + + transform.makeIdentity(); + transform.rotate3d(0, 100, 0); + EXPECT_TRUE(transform.isBackFaceVisible()); + + // Edge case, 90 degree rotation should return false. + transform.makeIdentity(); + transform.rotate3d(0, 90, 0); + EXPECT_FALSE(transform.isBackFaceVisible()); +} + +TEST(CCMathUtilTest, verifyBackfaceVisibilityForPerspective) +{ + TransformationMatrix layerSpaceToProjectionPlane; + + // This tests if isBackFaceVisible works properly under perspective transforms. + // Specifically, layers that may have their back face visible in orthographic + // projection, may not actually have back face visible under perspective projection. + + // Case 1: Layer is rotated by slightly more than 90 degrees, at the center of the + // prespective projection. In this case, the layer's back-side is visible to + // the camera. + layerSpaceToProjectionPlane.makeIdentity(); + layerSpaceToProjectionPlane.applyPerspective(1); + layerSpaceToProjectionPlane.translate3d(0, 0, 0); + layerSpaceToProjectionPlane.rotate3d(0, 100, 0); + EXPECT_TRUE(layerSpaceToProjectionPlane.isBackFaceVisible()); + + // Case 2: Layer is rotated by slightly more than 90 degrees, but shifted off to the + // side of the camera. Because of the wide field-of-view, the layer's front + // side is still visible. + // + // |<-- front side of layer is visible to perspective camera + // \ | / + // \ | / + // \| / + // | / + // |\ /<-- camera field of view + // | \ / + // back side of layer -->| \ / + // \./ <-- camera origin + // + layerSpaceToProjectionPlane.makeIdentity(); + layerSpaceToProjectionPlane.applyPerspective(1); + layerSpaceToProjectionPlane.translate3d(-10, 0, 0); + layerSpaceToProjectionPlane.rotate3d(0, 100, 0); + EXPECT_FALSE(layerSpaceToProjectionPlane.isBackFaceVisible()); + + // Case 3: Additionally rotating the layer by 180 degrees should of course show the + // opposite result of case 2. + layerSpaceToProjectionPlane.rotate3d(0, 180, 0); + EXPECT_TRUE(layerSpaceToProjectionPlane.isBackFaceVisible()); +} + +} // namespace diff --git a/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp b/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp index 26d52523b..73b4d8273 100644 --- a/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp +++ b/Source/WebKit/chromium/tests/CCOcclusionTrackerTest.cpp @@ -26,16 +26,23 @@ #include "cc/CCOcclusionTracker.h" +#include "CCAnimationTestCommon.h" +#include "CCOcclusionTrackerTestCommon.h" #include "FilterOperations.h" #include "LayerChromium.h" #include "Region.h" #include "TransformationMatrix.h" +#include "TranslateTransformOperation.h" +#include "cc/CCLayerAnimationController.h" +#include "cc/CCLayerImpl.h" #include "cc/CCLayerTreeHostCommon.h" +#include "cc/CCSingleThreadProxy.h" #include <gmock/gmock.h> #include <gtest/gtest.h> using namespace WebCore; +using namespace WebKitTests; #define EXPECT_EQ_RECT(a, b) \ EXPECT_EQ(a.x(), b.x()); \ @@ -45,2168 +52,2890 @@ using namespace WebCore; namespace { -class LayerChromiumWithForcedDrawsContent : public LayerChromium { +class TestContentLayerChromium : public LayerChromium { public: - LayerChromiumWithForcedDrawsContent() + TestContentLayerChromium() : LayerChromium() + , m_overrideOpaqueContentsRect(false) { } - virtual bool drawsContent() const { return true; } - virtual Region opaqueContentsRegion() const + virtual bool drawsContent() const OVERRIDE { return true; } + virtual Region visibleContentOpaqueRegion() const OVERRIDE { - return intersection(m_opaquePaintRect, visibleLayerRect()); + if (m_overrideOpaqueContentsRect) + return intersection(m_opaqueContentsRect, visibleLayerRect()); + return LayerChromium::visibleContentOpaqueRegion(); + } + void setOpaqueContentsRect(const IntRect& opaqueContentsRect) + { + m_overrideOpaqueContentsRect = true; + m_opaqueContentsRect = opaqueContentsRect; } - - void setOpaquePaintRect(const IntRect& opaquePaintRect) { m_opaquePaintRect = opaquePaintRect; } private: - IntRect m_opaquePaintRect; + bool m_overrideOpaqueContentsRect; + IntRect m_opaqueContentsRect; }; -void setLayerPropertiesForTesting(LayerChromium* layer, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds) -{ - layer->setTransform(transform); - layer->setSublayerTransform(TransformationMatrix()); - layer->setAnchorPoint(FloatPoint(0, 0)); - layer->setPosition(position); - layer->setBounds(bounds); -} - -void setLayerPropertiesForTesting(LayerChromiumWithForcedDrawsContent* layer, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds, bool opaque, bool opaqueLayers) -{ - setLayerPropertiesForTesting(layer, transform, position, bounds); - if (opaqueLayers) - layer->setOpaque(opaque); - else { - layer->setOpaque(false); - if (opaque) - layer->setOpaquePaintRect(IntRect(IntPoint(), bounds)); - else - layer->setOpaquePaintRect(IntRect()); - } -} - -// A subclass to expose the total current occlusion. -class TestCCOcclusionTracker : public CCOcclusionTracker { +class TestContentLayerImpl : public CCLayerImpl { public: - TestCCOcclusionTracker(IntRect screenScissorRect) - : CCOcclusionTracker(screenScissorRect) - , m_overrideLayerScissorRect(false) + TestContentLayerImpl(int id) + : CCLayerImpl(id) + , m_overrideOpaqueContentsRect(false) { + setDrawsContent(true); } - TestCCOcclusionTracker(IntRect screenScissorRect, const CCOcclusionTrackerDamageClient* damageClient) - : CCOcclusionTracker(screenScissorRect, damageClient) - , m_overrideLayerScissorRect(false) + virtual Region visibleContentOpaqueRegion() const OVERRIDE + { + if (m_overrideOpaqueContentsRect) + return intersection(m_opaqueContentsRect, visibleLayerRect()); + return CCLayerImpl::visibleContentOpaqueRegion(); + } + void setOpaqueContentsRect(const IntRect& opaqueContentsRect) { + m_overrideOpaqueContentsRect = true; + m_opaqueContentsRect = opaqueContentsRect; } - Region occlusionInScreenSpace() const { return CCOcclusionTracker::m_stack.last().occlusionInScreen; } - Region occlusionInTargetSurface() const { return CCOcclusionTracker::m_stack.last().occlusionInTarget; } +private: + bool m_overrideOpaqueContentsRect; + IntRect m_opaqueContentsRect; +}; - void setOcclusionInScreenSpace(const Region& region) { CCOcclusionTracker::m_stack.last().occlusionInScreen = region; } - void setOcclusionInTargetSurface(const Region& region) { CCOcclusionTracker::m_stack.last().occlusionInTarget = region; } +template<typename LayerType, typename RenderSurfaceType> +class TestCCOcclusionTrackerWithScissor : public TestCCOcclusionTrackerBase<LayerType, RenderSurfaceType> { +public: + TestCCOcclusionTrackerWithScissor(IntRect screenScissorRect, bool recordMetricsForFrame = false) + : TestCCOcclusionTrackerBase<LayerType, RenderSurfaceType>(screenScissorRect, recordMetricsForFrame) + , m_overrideLayerScissorRect(false) + { + } void setLayerScissorRect(const IntRect& rect) { m_overrideLayerScissorRect = true; m_layerScissorRect = rect;} void useDefaultLayerScissorRect() { m_overrideLayerScissorRect = false; } protected: - virtual IntRect layerScissorRectInTargetSurface(const LayerChromium* layer) const { return m_overrideLayerScissorRect ? m_layerScissorRect : CCOcclusionTracker::layerScissorRectInTargetSurface(layer); } + virtual IntRect layerScissorRectInTargetSurface(const LayerType* layer) const { return m_overrideLayerScissorRect ? m_layerScissorRect : CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::layerScissorRectInTargetSurface(layer); } private: bool m_overrideLayerScissorRect; IntRect m_layerScissorRect; }; -class TestDamageClient : public CCOcclusionTrackerDamageClient { -public: - // The interface - virtual FloatRect damageRect(const RenderSurfaceChromium*) const { return m_damageRect; } - - // Testing stuff - TestDamageClient(const FloatRect& damageRect) : m_damageRect(damageRect) { } - void setDamageRect(const FloatRect& damageRect) { m_damageRect = damageRect; } +struct CCOcclusionTrackerTestMainThreadTypes { + typedef LayerChromium LayerType; + typedef RenderSurfaceChromium RenderSurfaceType; + typedef TestContentLayerChromium ContentLayerType; + typedef RefPtr<LayerChromium> LayerPtrType; + typedef PassRefPtr<LayerChromium> PassLayerPtrType; + typedef RefPtr<ContentLayerType> ContentLayerPtrType; + typedef PassRefPtr<ContentLayerType> PassContentLayerPtrType; + typedef CCLayerIterator<LayerChromium, Vector<RefPtr<LayerChromium> >, RenderSurfaceChromium, CCLayerIteratorActions::FrontToBack> LayerIterator; + typedef CCOcclusionTracker OcclusionTrackerType; + + static PassLayerPtrType createLayer() { return LayerChromium::create(); } + static PassContentLayerPtrType createContentLayer() { return adoptRef(new ContentLayerType()); } +}; -private: - FloatRect m_damageRect; +struct CCOcclusionTrackerTestImplThreadTypes { + typedef CCLayerImpl LayerType; + typedef CCRenderSurface RenderSurfaceType; + typedef TestContentLayerImpl ContentLayerType; + typedef OwnPtr<CCLayerImpl> LayerPtrType; + typedef PassOwnPtr<CCLayerImpl> PassLayerPtrType; + typedef OwnPtr<ContentLayerType> ContentLayerPtrType; + typedef PassOwnPtr<ContentLayerType> PassContentLayerPtrType; + typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> LayerIterator; + typedef CCOcclusionTrackerImpl OcclusionTrackerType; + + static PassLayerPtrType createLayer() { return CCLayerImpl::create(nextCCLayerImplId++); } + static PassContentLayerPtrType createContentLayer() { return adoptPtr(new ContentLayerType(nextCCLayerImplId++)); } + static int nextCCLayerImplId; }; -#define TEST_OPAQUE_AND_PAINTED_OPAQUE(FULLTESTNAME, SHORTTESTNAME) \ - TEST(FULLTESTNAME, opaqueLayer) \ - { \ - SHORTTESTNAME(true); \ - } \ - TEST(FULLTESTNAME, opaquePaintRect) \ - { \ - SHORTTESTNAME(false); \ +int CCOcclusionTrackerTestImplThreadTypes::nextCCLayerImplId = 0; + +template<typename Types, bool opaqueLayers> +class CCOcclusionTrackerTest : public testing::Test { +protected: + CCOcclusionTrackerTest() + : testing::Test() + { } + + virtual void runMyTest() = 0; + + virtual void TearDown() + { + m_root.clear(); + m_renderSurfaceLayerListChromium.clear(); + m_renderSurfaceLayerListImpl.clear(); + m_replicaLayers.clear(); + CCLayerTreeHost::setNeedsFilterContext(false); } -void layerAddedToOccludedRegion(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(30, 30), IntSize(500, 500), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(31, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 31, 70, 70))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(31, 30, 70, 70))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 31, 70, 70))); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegion, layerAddedToOccludedRegion); - -void layerAddedToOccludedRegionWithRotation(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(layer.get(), layerTransform, FloatPoint(30, 30), IntSize(500, 500), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(31, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 31, 70, 70))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(31, 30, 70, 70))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 31, 70, 70))); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegionWithRotation, layerAddedToOccludedRegionWithRotation); - -void layerAddedToOccludedRegionWithTranslation(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); - - TransformationMatrix layerTransform; - layerTransform.translate(20, 20); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(layer.get(), layerTransform, FloatPoint(30, 30), IntSize(500, 500), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(parent.get(), IntRect(50, 50, 50, 50))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(49, 50, 50, 50))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(50, 49, 50, 50))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(51, 50, 50, 50))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(50, 51, 50, 50))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(50, 50, 50, 50))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(49, 50, 50, 50))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(50, 49, 50, 50))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(51, 50, 50, 50))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(50, 51, 50, 50))); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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))); - - occlusion.useDefaultLayerScissorRect(); - 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, 49, 1), occlusion.unoccludedContentRect(parent.get(), IntRect(51, 49, 50, 50))); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(51, 50, 50, 50)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(51, 51, 50, 50)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(50, 51, 50, 50)).isEmpty()); - EXPECT_EQ_RECT(IntRect(49, 51, 1, 49), occlusion.unoccludedContentRect(parent.get(), IntRect(49, 51, 50, 50))); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegionWithTranslation, layerAddedToOccludedRegionWithTranslation); - -void layerAddedToOccludedRegionWithRotatedSurface(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child.get(), childTransform, FloatPoint(30, 30), IntSize(500, 500)); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(10, 10), IntSize(500, 500), true, opaqueLayers); - - child->setMasksToBounds(true); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(10, 430, 61, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 71))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 61, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 71))); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 39, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(31, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 41, 70, 60))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 39, 70, 60))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(31, 40, 70, 60))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 41, 70, 60))); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - - /* 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_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegionWithRotatedSurface, layerAddedToOccludedRegionWithRotatedSurface); - -void layerAddedToOccludedRegionWithSurfaceAlreadyOnStack(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> child2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child.get(), childTransform, FloatPoint(30, 30), IntSize(500, 500)); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(10, 10), IntSize(500, 500), true, opaqueLayers); - - // |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, FloatPoint(30, 30), IntSize(60, 20), true, opaqueLayers); - - child->setMasksToBounds(true); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(-10, -10, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(11, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(10, 431, 60, 70))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(11, 430, 60, 70))); - EXPECT_TRUE(occlusion.occluded(child.get(), IntRect(10, 431, 60, 70))); - occlusion.setLayerScissorRect(IntRect(-10, -10, 1000, 1000)); - - 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))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(10, 430, 60, 70)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(9, 430, 60, 70)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(9, 430, 60, 80)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(-10, 430, 60, 70)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(-10, 430, 60, 80)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(10, 429, 60, 70)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(11, 430, 60, 70)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(child.get(), IntRect(10, 431, 60, 70)).isEmpty()); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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<IntRect> screen = occlusion.occlusionInScreenSpace().rects(); - Vector<IntRect> target = occlusion.occlusionInTargetSurface().rects(); - - EXPECT_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_TRUE(occlusion.occluded(parent.get(), IntRect(30, 30, 60, 10))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 30, 60, 10))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 29, 60, 10))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(31, 30, 60, 10))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 31, 60, 10))); - - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); - EXPECT_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))); - - /* 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_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegionWithSurfaceAlreadyOnStack, layerAddedToOccludedRegionWithSurfaceAlreadyOnStack); - -void layerAddedToOccludedRegionWithRotatedOffAxisSurface(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child.get(), childTransform, FloatPoint(30, 30), IntSize(500, 500)); - setLayerPropertiesForTesting(layer.get(), layerTransform, FloatPoint(0, 0), IntSize(500, 500), true, opaqueLayers); - - child->setMasksToBounds(true); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child.get(), clippedLayerInChild)); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); - clippedLayerInChild.move(-1, 0); - EXPECT_FALSE(occlusion.occluded(child.get(), clippedLayerInChild)); - EXPECT_FALSE(occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); - clippedLayerInChild.move(1, 0); - clippedLayerInChild.move(1, 0); - EXPECT_FALSE(occlusion.occluded(child.get(), clippedLayerInChild)); - EXPECT_FALSE(occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); - clippedLayerInChild.move(-1, 0); - clippedLayerInChild.move(0, -1); - EXPECT_FALSE(occlusion.occluded(child.get(), clippedLayerInChild)); - EXPECT_FALSE(occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); - clippedLayerInChild.move(0, 1); - clippedLayerInChild.move(0, 1); - EXPECT_FALSE(occlusion.occluded(child.get(), clippedLayerInChild)); - EXPECT_FALSE(occlusion.unoccludedContentRect(parent.get(), clippedLayerInChild).isEmpty()); - clippedLayerInChild.move(0, -1); - - 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_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))); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegionWithRotatedOffAxisSurface, layerAddedToOccludedRegionWithRotatedOffAxisSurface); - -void layerAddedToOccludedRegionWithMultipleOpaqueLayers(bool opaqueLayers) -{ - // 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. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child.get(), childTransform, FloatPoint(30, 30), IntSize(500, 500)); - setLayerPropertiesForTesting(layer1.get(), identityMatrix, FloatPoint(10, 10), IntSize(500, 440), true, opaqueLayers); - setLayerPropertiesForTesting(layer2.get(), identityMatrix, FloatPoint(10, 450), IntSize(500, 60), true, opaqueLayers); - - child->setMasksToBounds(true); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child.get(), IntRect(10, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(9, 430, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(10, 429, 60, 70))); - EXPECT_FALSE(occlusion.occluded(child.get(), IntRect(11, 430, 60, 70))); - EXPECT_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))); - - 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_TRUE(occlusion.occluded(parent.get(), IntRect(30, 40, 70, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 40, 70, 60))); - EXPECT_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))); - - /* 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_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerAddedToOccludedRegionWithMultipleOpaqueLayers, layerAddedToOccludedRegionWithMultipleOpaqueLayers); - -void surfaceOcclusionWithOverlappingSiblingSurfaces(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child1 = LayerChromium::create(); - RefPtr<LayerChromium> child2 = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child1.get(), childTransform, FloatPoint(30, 30), IntSize(500, 500)); - setLayerPropertiesForTesting(layer1.get(), identityMatrix, FloatPoint(0, 0), IntSize(500, 500), true, opaqueLayers); - setLayerPropertiesForTesting(child2.get(), childTransform, FloatPoint(20, 40), IntSize(500, 500)); - setLayerPropertiesForTesting(layer2.get(), identityMatrix, FloatPoint(0, 0), IntSize(500, 500), true, opaqueLayers); - - child1->setMasksToBounds(true); - child2->setMasksToBounds(true); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(-10, -10, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child2.get(), IntRect(0, 420, 60, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-1, 420, 60, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(0, 419, 60, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(0, 420, 61, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(0, 420, 60, 81))); - - 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_TRUE(occlusion.occluded(child1.get(), IntRect(0, 430, 70, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(-1, 430, 70, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(0, 429, 70, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(0, 430, 71, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(0, 430, 70, 71))); - - 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_FALSE(occlusion.occluded(parent.get(), IntRect(20, 30, 80, 70))); - - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 30, 70, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 29, 70, 70))); - - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(20, 40, 80, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(19, 40, 80, 60))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(20, 39, 80, 60))); - - /* Justification for the above occlusion: - 100 - +---------------------+ - | | - | 30 | child1 - | 30+ ---------------------------------+ - 100 | 40| | child2 | - |20+----------------------------------+ | - | | | | | | - | | | | | | - | | | | | | - +--|-|----------------+ | | - | | | | 500 - | | | | - | | | | - | | | | - | | | | - | | | | - | | | | - | +--------------------------------|-+ - | | - +----------------------------------+ - 500 - */ -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_surfaceOcclusionWithOverlappingSiblingSurfaces, surfaceOcclusionWithOverlappingSiblingSurfaces); - -void surfaceOcclusionInScreenSpace(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child1 = LayerChromium::create(); - RefPtr<LayerChromium> child2 = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child1.get(), childTransform, FloatPoint(30, 30), IntSize(10, 10)); - setLayerPropertiesForTesting(layer1.get(), identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true, opaqueLayers); - setLayerPropertiesForTesting(child2.get(), childTransform, FloatPoint(20, 40), IntSize(10, 10)); - setLayerPropertiesForTesting(layer2.get(), identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true, opaqueLayers); - - // 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<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(-20, -20, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-11, 420, 70, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-10, 419, 70, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-10, 420, 71, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 81))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 80))); - EXPECT_TRUE(occlusion.occluded(child2.get(), IntRect(-11, 420, 70, 80))); - EXPECT_TRUE(occlusion.occluded(child2.get(), IntRect(-10, 419, 70, 80))); - EXPECT_TRUE(occlusion.occluded(child2.get(), IntRect(-10, 420, 71, 80))); - EXPECT_TRUE(occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 81))); - occlusion.setLayerScissorRect(IntRect(-20, -20, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child1.get(), IntRect(-10, 430, 80, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(-11, 430, 80, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(-10, 429, 80, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(-10, 430, 81, 70))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(-10, 430, 80, 71))); - - 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_FALSE(occlusion.occluded(parent.get(), IntRect(20, 20, 80, 80))); - - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(30, 20, 70, 80))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(29, 20, 70, 80))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(30, 19, 70, 80))); - - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(20, 30, 80, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(19, 30, 80, 70))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(20, 29, 80, 70))); - - /* Justification for the above occlusion: - 100 - +---------------------+ - | 20 | layer1 - | 30+ ---------------------------------+ - 100 | 30| | layer2 | - |20+----------------------------------+ | - | | | | | | - | | | | | | - | | | | | | - +--|-|----------------+ | | - | | | | 510 - | | | | - | | | | - | | | | - | | | | - | | | | - | | | | - | +--------------------------------|-+ - | | - +----------------------------------+ - 510 - */ -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_surfaceOcclusionInScreenSpace, surfaceOcclusionInScreenSpace); - -void surfaceOcclusionInScreenSpaceDifferentTransforms(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromium> child1 = LayerChromium::create(); - RefPtr<LayerChromium> child2 = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer1 = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer2 = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(child1); - parent->addChild(child2); - child1->addChild(layer1); - child2->addChild(layer2); - - TransformationMatrix child1Transform; - child1Transform.translate(250, 250); - child1Transform.rotate(-90); - child1Transform.translate(-250, -250); - - TransformationMatrix child2Transform; - child2Transform.translate(250, 250); - child2Transform.rotate(90); - child2Transform.translate(-250, -250); - - // The owning layers have very different bounds from the surfaces that they own. - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(child1.get(), child1Transform, FloatPoint(30, 20), IntSize(10, 10)); - setLayerPropertiesForTesting(layer1.get(), identityMatrix, FloatPoint(-10, -20), IntSize(510, 520), true, opaqueLayers); - setLayerPropertiesForTesting(child2.get(), child2Transform, FloatPoint(20, 40), IntSize(10, 10)); - setLayerPropertiesForTesting(layer2.get(), identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true, opaqueLayers); - - // 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<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(-30, -30, 1000, 1000)); - - 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_TRUE(occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-11, 420, 70, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-10, 419, 70, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-10, 420, 71, 80))); - EXPECT_FALSE(occlusion.occluded(child2.get(), IntRect(-10, 420, 70, 81))); - - 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(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds()); - EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(420, -20, 80, 90), occlusion.occlusionInTargetSurface().bounds()); - EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); - - EXPECT_TRUE(occlusion.occluded(child1.get(), IntRect(420, -20, 80, 90))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(419, -20, 80, 90))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(420, -21, 80, 90))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(420, -19, 80, 90))); - EXPECT_FALSE(occlusion.occluded(child1.get(), IntRect(421, -20, 80, 90))); - - occlusion.markOccludedBehindLayer(child1.get()); - occlusion.finishedTargetRenderSurface(child1.get(), child1->renderSurface()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_EQ_RECT(IntRect(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds()); - EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); - EXPECT_EQ_RECT(IntRect(10, 20, 90, 80), occlusion.occlusionInTargetSurface().bounds()); - EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); - - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(10, 20, 90, 80))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(9, 20, 90, 80))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(10, 19, 90, 80))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(11, 20, 90, 80))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(10, 21, 90, 80))); - - /* Justification for the above occlusion: - 100 - +---------------------+ - |20 | layer1 - 10+----------------------------------+ - 100 || 30 | layer2 | - |20+----------------------------------+ - || | | | | - || | | | | - || | | | | - +|-|------------------+ | | - | | | | 510 - | | 510 | | - | | | | - | | | | - | | | | - | | | | - | | 520 | | - +----------------------------------+ | - | | - +----------------------------------+ - 510 - */ -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_surfaceOcclusionInScreenSpaceDifferentTransforms, surfaceOcclusionInScreenSpaceDifferentTransforms); - -void occlusionInteractionWithFilters(bool opaqueLayers) -{ - // This tests that the right transforms are being used. - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> blurLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> opacityLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> 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, FloatPoint(0, 0), IntSize(100, 100)); - setLayerPropertiesForTesting(blurLayer.get(), layerTransform, FloatPoint(30, 30), IntSize(500, 500), true, opaqueLayers); - setLayerPropertiesForTesting(opaqueLayer.get(), layerTransform, FloatPoint(30, 30), IntSize(500, 500), true, opaqueLayers); - setLayerPropertiesForTesting(opacityLayer.get(), layerTransform, FloatPoint(30, 30), IntSize(500, 500), true, opaqueLayers); + typename Types::ContentLayerType* createRoot(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); + + ASSERT(!m_root); + m_root = layer.release(); + return layerPtr; + } + + typename Types::LayerType* createLayer(typename Types::LayerType* parent, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds) + { + typename Types::LayerPtrType layer(Types::createLayer()); + typename Types::LayerType* layerPtr = layer.get(); + setProperties(layerPtr, transform, position, bounds); + parent->addChild(layer.release()); + return layerPtr; + } + typename Types::LayerType* createSurface(typename Types::LayerType* parent, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds) { + typename Types::LayerType* layer = createLayer(parent, transform, position, bounds); FilterOperations filters; - filters.operations().append(BlurFilterOperation::create(Length(10, WebCore::Percent), FilterOperation::BLUR)); - blurLayer->setFilters(filters); + filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); + layer->setFilters(filters); + return layer; } + typename Types::ContentLayerType* createDrawingLayer(typename Types::LayerType* parent, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds, bool opaque) { + typename Types::ContentLayerPtrType layer(Types::createContentLayer()); + typename Types::ContentLayerType* layerPtr = layer.get(); + setProperties(layerPtr, transform, position, bounds); + + if (opaqueLayers) + layerPtr->setOpaque(opaque); + else { + layerPtr->setOpaque(false); + if (opaque) + layerPtr->setOpaqueContentsRect(IntRect(IntPoint(), bounds)); + else + layerPtr->setOpaqueContentsRect(IntRect()); + } + + parent->addChild(layer.release()); + 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); FilterOperations filters; filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - opaqueLayer->setFilters(filters); + layer->setFilters(filters); + return layer; + } + + void calcDrawEtc(TestContentLayerImpl* root) + { + ASSERT(root == m_root.get()); + Vector<CCLayerImpl*> dummyLayerList; + int dummyMaxTextureSize = 512; + + ASSERT(!root->renderSurface()); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), root->bounds())); + root->setClipRect(IntRect(IntPoint::zero(), root->bounds())); + m_renderSurfaceLayerListImpl.append(m_root.get()); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(root, root, identityMatrix, identityMatrix, m_renderSurfaceLayerListImpl, dummyLayerList, 0, dummyMaxTextureSize); + m_layerIterator = m_layerIteratorBegin = Types::LayerIterator::begin(&m_renderSurfaceLayerListImpl); + } + + void calcDrawEtc(TestContentLayerChromium* root) + { + ASSERT(root == m_root.get()); + Vector<RefPtr<LayerChromium> > dummyLayerList; + int dummyMaxTextureSize = 512; + + ASSERT(!root->renderSurface()); + root->createRenderSurface(); + root->renderSurface()->setContentRect(IntRect(IntPoint::zero(), root->bounds())); + root->setClipRect(IntRect(IntPoint::zero(), root->bounds())); + m_renderSurfaceLayerListChromium.append(m_root); + + CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(root, root, identityMatrix, identityMatrix, m_renderSurfaceLayerListChromium, dummyLayerList, dummyMaxTextureSize); + m_layerIterator = m_layerIteratorBegin = Types::LayerIterator::begin(&m_renderSurfaceLayerListChromium); } + void enterLayer(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion) { + ASSERT_EQ(layer, *m_layerIterator); + ASSERT_TRUE(m_layerIterator.representsItself()); + occlusion.enterLayer(m_layerIterator); + } + + void leaveLayer(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion) + { + ASSERT_EQ(layer, *m_layerIterator); + ASSERT_TRUE(m_layerIterator.representsItself()); + occlusion.leaveLayer(m_layerIterator); + ++m_layerIterator; + } + + void visitLayer(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion) + { + enterLayer(layer, occlusion); + leaveLayer(layer, occlusion); + } + + void enterContributingSurface(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion) + { + ASSERT_EQ(layer, *m_layerIterator); + ASSERT_TRUE(m_layerIterator.representsTargetRenderSurface()); + occlusion.enterLayer(m_layerIterator); + occlusion.leaveLayer(m_layerIterator); + ++m_layerIterator; + ASSERT_TRUE(m_layerIterator.representsContributingRenderSurface()); + occlusion.enterLayer(m_layerIterator); + } + + void leaveContributingSurface(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion) + { + ASSERT_EQ(layer, *m_layerIterator); + ASSERT_TRUE(m_layerIterator.representsContributingRenderSurface()); + occlusion.leaveLayer(m_layerIterator); + ++m_layerIterator; + } + + void visitContributingSurface(typename Types::LayerType* layer, typename Types::OcclusionTrackerType& occlusion) + { + enterContributingSurface(layer, occlusion); + leaveContributingSurface(layer, occlusion); + } + + void resetLayerIterator() + { + m_layerIterator = m_layerIteratorBegin; + } + + const TransformationMatrix identityMatrix; + +private: + void setBaseProperties(typename Types::LayerType* layer, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds) + { + layer->setTransform(transform); + layer->setSublayerTransform(TransformationMatrix()); + layer->setAnchorPoint(FloatPoint(0, 0)); + layer->setPosition(position); + layer->setBounds(bounds); + } + + void setProperties(LayerChromium* layer, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds) + { + setBaseProperties(layer, transform, position, bounds); + } + + void setProperties(CCLayerImpl* layer, const TransformationMatrix& transform, const FloatPoint& position, const IntSize& bounds) + { + setBaseProperties(layer, transform, position, bounds); + + 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; + typename Types::LayerIterator m_layerIteratorBegin; + typename Types::LayerIterator m_layerIterator; + typename Types::LayerType* m_lastLayerVisited; + Vector<RefPtr<LayerChromium> > m_replicaLayers; +}; + +#define RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \ + class ClassName##MainThreadOpaqueLayers : public ClassName<CCOcclusionTrackerTestMainThreadTypes, true> { \ + public: \ + ClassName##MainThreadOpaqueLayers() : ClassName<CCOcclusionTrackerTestMainThreadTypes, true>() { } \ + }; \ + TEST_F(ClassName##MainThreadOpaqueLayers, runTest) { runMyTest(); } +#define RUN_TEST_MAIN_THREAD_OPAQUE_PAINTS(ClassName) \ + class ClassName##MainThreadOpaquePaints : public ClassName<CCOcclusionTrackerTestMainThreadTypes, false> { \ + public: \ + ClassName##MainThreadOpaquePaints() : ClassName<CCOcclusionTrackerTestMainThreadTypes, false>() { } \ + }; \ + TEST_F(ClassName##MainThreadOpaquePaints, runTest) { runMyTest(); } + +#define RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName) \ + class ClassName##ImplThreadOpaqueLayers : public ClassName<CCOcclusionTrackerTestImplThreadTypes, true> { \ + DebugScopedSetImplThread impl; \ + public: \ + ClassName##ImplThreadOpaqueLayers() : ClassName<CCOcclusionTrackerTestImplThreadTypes, true>() { } \ + }; \ + TEST_F(ClassName##ImplThreadOpaqueLayers, runTest) { runMyTest(); } +#define RUN_TEST_IMPL_THREAD_OPAQUE_PAINTS(ClassName) \ + class ClassName##ImplThreadOpaquePaints : public ClassName<CCOcclusionTrackerTestImplThreadTypes, false> { \ + DebugScopedSetImplThread impl; \ + public: \ + ClassName##ImplThreadOpaquePaints() : ClassName<CCOcclusionTrackerTestImplThreadTypes, false>() { } \ + }; \ + TEST_F(ClassName##ImplThreadOpaquePaints, runTest) { runMyTest(); } + +#define ALL_CCOCCLUSIONTRACKER_TEST(ClassName) \ + RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \ + RUN_TEST_MAIN_THREAD_OPAQUE_PAINTS(ClassName) \ + RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName) \ + RUN_TEST_IMPL_THREAD_OPAQUE_PAINTS(ClassName) + +#define MAIN_THREAD_TEST(ClassName) \ + RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) + +#define MAIN_AND_IMPL_THREAD_TEST(ClassName) \ + RUN_TEST_MAIN_THREAD_OPAQUE_LAYERS(ClassName) \ + RUN_TEST_IMPL_THREAD_OPAQUE_LAYERS(ClassName) + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestIdentityTransforms : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(30, 30), IntSize(500, 500), 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->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + + 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_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 31, 70, 70))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(31, 30, 70, 70))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 31, 70, 70))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 30, 70, 70)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 29, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(100, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 31, 70, 70))); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestIdentityTransforms); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestRotatedChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix layerTransform; + layerTransform.translate(250, 250); + layerTransform.rotate(90); + layerTransform.translate(-250, -250); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), 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->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + + 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_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 31, 70, 70))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 70, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 70, 70))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(31, 30, 70, 70))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 31, 70, 70))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 30, 70, 70)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 29, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 29, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 29, 70, 70))); + EXPECT_EQ_RECT(IntRect(100, 30, 1, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(31, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(31, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 31, 70, 70))); + EXPECT_EQ_RECT(IntRect(29, 31, 70, 70), occlusion.unoccludedContentRect(parent, IntRect(29, 31, 70, 70))); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestRotatedChild); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestTranslatedChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix layerTransform; + layerTransform.translate(20, 20); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), 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->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + + 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_TRUE(occlusion.occluded(parent, IntRect(50, 50, 50, 50))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(49, 50, 50, 50))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(50, 49, 50, 50))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(51, 50, 50, 50))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(50, 51, 50, 50))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(50, 50, 50, 50))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(49, 50, 50, 50))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(50, 49, 50, 50))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(51, 50, 50, 50))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(50, 51, 50, 50))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(50, 50, 50, 50)).isEmpty()); + EXPECT_EQ_RECT(IntRect(49, 50, 1, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 50, 50, 50))); + EXPECT_EQ_RECT(IntRect(49, 49, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(50, 49, 50, 1), occlusion.unoccludedContentRect(parent, IntRect(50, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(51, 49, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(51, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(100, 50, 1, 50), occlusion.unoccludedContentRect(parent, IntRect(51, 50, 50, 50))); + EXPECT_EQ_RECT(IntRect(51, 51, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(51, 51, 50, 50))); + EXPECT_EQ_RECT(IntRect(50, 100, 50, 1), occlusion.unoccludedContentRect(parent, IntRect(50, 51, 50, 50))); + EXPECT_EQ_RECT(IntRect(49, 51, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 51, 50, 50))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(50, 50, 50, 50)).isEmpty()); + EXPECT_EQ_RECT(IntRect(49, 50, 1, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 50, 50, 50))); + EXPECT_EQ_RECT(IntRect(49, 49, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(49, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(50, 49, 50, 1), occlusion.unoccludedContentRect(parent, IntRect(50, 49, 50, 50))); + EXPECT_EQ_RECT(IntRect(51, 49, 49, 1), occlusion.unoccludedContentRect(parent, IntRect(51, 49, 50, 50))); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(51, 50, 50, 50)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(51, 51, 50, 50)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(50, 51, 50, 50)).isEmpty()); + EXPECT_EQ_RECT(IntRect(49, 51, 1, 49), occlusion.unoccludedContentRect(parent, IntRect(49, 51, 50, 50))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestTranslatedChild); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestChildInRotatedChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500)); + child->setMasksToBounds(true); + typename Types::ContentLayerType* layer = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 10), IntSize(500, 500), 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->visitLayer(layer, occlusion); + this->enterContributingSurface(child, occlusion); + + 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_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(9, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 429, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 430, 61, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 430, 60, 71))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(9, 430, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 429, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 430, 61, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 71))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->leaveContributingSurface(child, occlusion); + this->enterLayer(parent, occlusion); + + 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_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 41, 70, 60))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(31, 40, 70, 60))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 41, 70, 60))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + + /* 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 + */ + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestChildInRotatedChild); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestVisitTargetTwoTimes : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500)); + child->setMasksToBounds(true); + typename Types::ContentLayerType* layer = this->createDrawingLayer(child, this->identityMatrix, 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. + typename Types::ContentLayerType* child2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(30, 30), IntSize(60, 20), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(-10, -10, 1000, 1000)); + + this->visitLayer(child2, occlusion); + + 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()); + + this->visitLayer(layer, occlusion); + + 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()); + + this->enterContributingSurface(child, occlusion); + + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(9, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 429, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(11, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 431, 60, 70))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(9, 430, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 429, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(11, 430, 60, 70))); + EXPECT_TRUE(occlusion.occluded(child, IntRect(10, 431, 60, 70))); + occlusion.setLayerScissorRect(IntRect(-10, -10, 1000, 1000)); + + EXPECT_TRUE(occlusion.unoccludedContentRect(child, 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, 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, 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, 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, IntRect(-10, 430, 60, 80))); + EXPECT_EQ_RECT(IntRect(10, 429, 60, 1), occlusion.unoccludedContentRect(child, IntRect(10, 429, 60, 70))); + EXPECT_EQ_RECT(IntRect(70, 430, 1, 70), occlusion.unoccludedContentRect(child, IntRect(11, 430, 60, 70))); + EXPECT_EQ_RECT(IntRect(10, 500, 60, 1), occlusion.unoccludedContentRect(child, IntRect(10, 431, 60, 70))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(10, 430, 60, 70)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(9, 430, 60, 70)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(9, 430, 60, 80)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(-10, 430, 60, 70)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(-10, 430, 60, 80)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(10, 429, 60, 70)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(11, 430, 60, 70)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(10, 431, 60, 70)).isEmpty()); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + // Occlusion in |child2| should get merged with the |child| surface we are leaving now. + this->leaveContributingSurface(child, occlusion); + this->enterLayer(parent, occlusion); + + 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()); + + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 30, 70, 70))); + EXPECT_EQ_RECT(IntRect(90, 30, 10, 10), occlusion.unoccludedContentRect(parent, IntRect(30, 30, 70, 70))); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 30, 60, 10))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 30, 60, 10))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 29, 60, 10))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(31, 30, 60, 10))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 31, 60, 10))); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 30, 60, 10)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 30, 1, 10), occlusion.unoccludedContentRect(parent, IntRect(29, 30, 60, 10))); + EXPECT_EQ_RECT(IntRect(30, 29, 60, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 29, 60, 10))); + EXPECT_EQ_RECT(IntRect(90, 30, 1, 10), occlusion.unoccludedContentRect(parent, IntRect(31, 30, 60, 10))); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 31, 60, 10)).isEmpty()); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 40, 70, 60)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(29, 40, 70, 60))); + // This rect is mostly occluded by |child2|. + EXPECT_EQ_RECT(IntRect(90, 39, 10, 1), occlusion.unoccludedContentRect(parent, 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, 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, IntRect(20, 39, 80, 60))); + EXPECT_EQ_RECT(IntRect(100, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(31, 40, 70, 60))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 41, 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 + */ + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestVisitTargetTwoTimes); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestSurfaceRotatedOffAxis : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(95); + childTransform.translate(-250, -250); + + TransformationMatrix layerTransform; + layerTransform.translate(10, 10); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500)); + child->setMasksToBounds(true); + typename Types::ContentLayerType* layer = this->createDrawingLayer(child, layerTransform, FloatPoint(0, 0), IntSize(500, 500), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + IntRect clippedLayerInChild = layerTransform.mapRect(layer->visibleLayerRect()); + + this->visitLayer(layer, occlusion); + this->enterContributingSurface(child, occlusion); + + 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_TRUE(occlusion.occluded(child, clippedLayerInChild)); + EXPECT_TRUE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(-1, 0); + EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild)); + EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(1, 0); + clippedLayerInChild.move(1, 0); + EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild)); + EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(-1, 0); + clippedLayerInChild.move(0, -1); + EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild)); + EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(0, 1); + clippedLayerInChild.move(0, 1); + EXPECT_FALSE(occlusion.occluded(child, clippedLayerInChild)); + EXPECT_FALSE(occlusion.unoccludedContentRect(child, clippedLayerInChild).isEmpty()); + clippedLayerInChild.move(0, -1); + + this->leaveContributingSurface(child, occlusion); + this->enterLayer(parent, occlusion); + + 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_FALSE(occlusion.occluded(parent, IntRect(75, 55, 1, 1))); + EXPECT_EQ_RECT(IntRect(75, 55, 1, 1), occlusion.unoccludedContentRect(parent, IntRect(75, 55, 1, 1))); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceRotatedOffAxis); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestSurfaceWithTwoOpaqueChildren : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::LayerType* child = this->createLayer(parent, childTransform, FloatPoint(30, 30), IntSize(500, 500)); + child->setMasksToBounds(true); + typename Types::ContentLayerType* layer1 = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 10), IntSize(500, 500), true); + typename Types::ContentLayerType* layer2 = this->createDrawingLayer(child, this->identityMatrix, FloatPoint(10, 450), IntSize(500, 60), 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->visitLayer(layer2, occlusion); + this->visitLayer(layer1, occlusion); + this->enterContributingSurface(child, occlusion); + + 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_TRUE(occlusion.occluded(child, IntRect(10, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(9, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 429, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(11, 430, 60, 70))); + EXPECT_FALSE(occlusion.occluded(child, IntRect(10, 431, 60, 70))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(child, IntRect(10, 430, 60, 70)).isEmpty()); + EXPECT_EQ_RECT(IntRect(9, 430, 1, 70), occlusion.unoccludedContentRect(child, IntRect(9, 430, 60, 70))); + EXPECT_EQ_RECT(IntRect(10, 429, 60, 1), occlusion.unoccludedContentRect(child, IntRect(10, 429, 60, 70))); + EXPECT_EQ_RECT(IntRect(70, 430, 1, 70), occlusion.unoccludedContentRect(child, IntRect(11, 430, 60, 70))); + EXPECT_EQ_RECT(IntRect(10, 500, 60, 1), occlusion.unoccludedContentRect(child, IntRect(10, 431, 60, 70))); + + this->leaveContributingSurface(child, occlusion); + this->enterLayer(parent, occlusion); + + 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_TRUE(occlusion.occluded(parent, IntRect(30, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 40, 70, 60))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 39, 70, 60))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(30, 40, 70, 60)).isEmpty()); + EXPECT_EQ_RECT(IntRect(29, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(29, 40, 70, 60))); + EXPECT_EQ_RECT(IntRect(30, 39, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 39, 70, 60))); + EXPECT_EQ_RECT(IntRect(100, 40, 1, 60), occlusion.unoccludedContentRect(parent, IntRect(31, 40, 70, 60))); + EXPECT_EQ_RECT(IntRect(30, 100, 70, 1), occlusion.unoccludedContentRect(parent, IntRect(30, 41, 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 + */ + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceWithTwoOpaqueChildren); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestOverlappingSurfaceSiblings : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix childTransform; + childTransform.translate(250, 250); + childTransform.rotate(90); + childTransform.translate(-250, -250); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::LayerType* child1 = this->createSurface(parent, childTransform, FloatPoint(30, 30), IntSize(10, 10)); + typename Types::LayerType* child2 = this->createSurface(parent, childTransform, FloatPoint(20, 40), IntSize(10, 10)); + typename Types::ContentLayerType* layer1 = this->createDrawingLayer(child1, this->identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true); + typename Types::ContentLayerType* layer2 = this->createDrawingLayer(child2, this->identityMatrix, FloatPoint(-10, -10), IntSize(510, 510), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(-20, -20, 1000, 1000)); + + this->visitLayer(layer2, occlusion); + this->enterContributingSurface(child2, occlusion); + + 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_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-11, 420, 70, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 419, 70, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 71, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 70, 81))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 80))); + EXPECT_TRUE(occlusion.occluded(child2, IntRect(-11, 420, 70, 80))); + EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 419, 70, 80))); + EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 71, 80))); + EXPECT_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 81))); + occlusion.setLayerScissorRect(IntRect(-20, -20, 1000, 1000)); + + // There is nothing above child2's surface in the z-order. + EXPECT_EQ_RECT(IntRect(-10, 420, 70, 80), occlusion.unoccludedContributingSurfaceContentRect(child2->renderSurface(), false, IntRect(-10, 420, 70, 80))); + + this->leaveContributingSurface(child2, occlusion); + this->visitLayer(layer1, occlusion); + this->enterContributingSurface(child1, occlusion); + + 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_TRUE(occlusion.occluded(child1, IntRect(-10, 430, 80, 70))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(-11, 430, 80, 70))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(-10, 429, 80, 70))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(-10, 430, 81, 70))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(-10, 430, 80, 71))); + + // child2's contents will occlude child1 below it. + EXPECT_EQ_RECT(IntRect(-10, 430, 10, 70), occlusion.unoccludedContributingSurfaceContentRect(child1->renderSurface(), false, IntRect(-10, 430, 80, 70))); + + this->leaveContributingSurface(child1, occlusion); + this->enterLayer(parent, occlusion); + + 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_FALSE(occlusion.occluded(parent, IntRect(20, 20, 80, 80))); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(30, 20, 70, 80))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(29, 20, 70, 80))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(30, 19, 70, 80))); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(20, 30, 80, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(19, 30, 80, 70))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(20, 29, 80, 70))); + + /* Justification for the above occlusion: + 100 + +---------------------+ + | 20 | layer1 + | 30+ ---------------------------------+ + 100 | 30| | layer2 | + |20+----------------------------------+ | + | | | | | | + | | | | | | + | | | | | | + +--|-|----------------+ | | + | | | | 510 + | | | | + | | | | + | | | | + | | | | + | | | | + | | | | + | +--------------------------------|-+ + | | + +----------------------------------+ + 510 + */ + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestOverlappingSurfaceSiblings); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestOverlappingSurfaceSiblingsWithTwoTransforms : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix child1Transform; + child1Transform.translate(250, 250); + child1Transform.rotate(-90); + child1Transform.translate(-250, -250); + + TransformationMatrix child2Transform; + child2Transform.translate(250, 250); + child2Transform.rotate(90); + child2Transform.translate(-250, -250); + + 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->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); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(-30, -30, 1000, 1000)); + + this->visitLayer(layer2, occlusion); + this->enterLayer(child2, occlusion); + + 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_TRUE(occlusion.occluded(child2, IntRect(-10, 420, 70, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-11, 420, 70, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 419, 70, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 71, 80))); + EXPECT_FALSE(occlusion.occluded(child2, IntRect(-10, 420, 70, 81))); + + this->leaveLayer(child2, occlusion); + this->enterContributingSurface(child2, occlusion); + + // There is nothing above child2's surface in the z-order. + EXPECT_EQ_RECT(IntRect(-10, 420, 70, 80), occlusion.unoccludedContributingSurfaceContentRect(child2->renderSurface(), false, IntRect(-10, 420, 70, 80))); + + this->leaveContributingSurface(child2, occlusion); + this->visitLayer(layer1, occlusion); + this->enterContributingSurface(child1, occlusion); + + EXPECT_EQ_RECT(IntRect(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(420, -20, 80, 90), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_TRUE(occlusion.occluded(child1, IntRect(420, -20, 80, 90))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(419, -20, 80, 90))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(420, -21, 80, 90))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(420, -19, 80, 90))); + EXPECT_FALSE(occlusion.occluded(child1, IntRect(421, -20, 80, 90))); + + // child2's contents will occlude child1 below it. + EXPECT_EQ_RECT(IntRect(420, -20, 80, 90), occlusion.unoccludedContributingSurfaceContentRect(child1->renderSurface(), false, IntRect(420, -20, 80, 90))); + EXPECT_EQ_RECT(IntRect(490, -10, 10, 80), occlusion.unoccludedContributingSurfaceContentRect(child1->renderSurface(), false, IntRect(420, -10, 80, 90))); + EXPECT_EQ_RECT(IntRect(420, -20, 70, 10), occlusion.unoccludedContributingSurfaceContentRect(child1->renderSurface(), false, IntRect(420, -20, 70, 90))); + + this->leaveContributingSurface(child1, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_EQ_RECT(IntRect(10, 20, 90, 80), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(10, 20, 90, 80), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(10, 20, 90, 80))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(9, 20, 90, 80))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(10, 19, 90, 80))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(11, 20, 90, 80))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(10, 21, 90, 80))); + + /* Justification for the above occlusion: + 100 + +---------------------+ + |20 | layer1 + 10+----------------------------------+ + 100 || 30 | layer2 | + |20+----------------------------------+ + || | | | | + || | | | | + || | | | | + +|-|------------------+ | | + | | | | 510 + | | 510 | | + | | | | + | | | | + | | | | + | | | | + | | 520 | | + +----------------------------------+ | + | | + +----------------------------------+ + 510 + */ + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestOverlappingSurfaceSiblingsWithTwoTransforms); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestFilters : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix layerTransform; + layerTransform.translate(250, 250); + layerTransform.rotate(90); + layerTransform.translate(-250, -250); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::ContentLayerType* blurLayer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true); + typename Types::ContentLayerType* opaqueLayer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true); + typename Types::ContentLayerType* opacityLayer = this->createDrawingLayer(parent, layerTransform, FloatPoint(30, 30), IntSize(500, 500), true); + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(10, WebCore::Percent), FilterOperation::BLUR)); + blurLayer->setFilters(filters); + + filters.operations().clear(); + filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); + opaqueLayer->setFilters(filters); + + filters.operations().clear(); filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::OPACITY)); opacityLayer->setFilters(filters); + + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + // Opacity layer won't contribute to occlusion. + this->visitLayer(opacityLayer, occlusion); + this->enterContributingSurface(opacityLayer, occlusion); + + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // And has nothing to contribute to its parent surface. + this->leaveContributingSurface(opacityLayer, occlusion); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // Opaque layer will contribute to occlusion. + this->visitLayer(opaqueLayer, occlusion); + this->enterContributingSurface(opaqueLayer, occlusion); + + 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. + this->leaveContributingSurface(opaqueLayer, occlusion); + 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. + this->enterLayer(blurLayer, occlusion); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // And it won't contribute to occlusion. + this->leaveLayer(blurLayer, occlusion); + this->enterContributingSurface(blurLayer, occlusion); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); + EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + + // But the opaque layer's occlusion is preserved on the parent. + this->leaveContributingSurface(blurLayer, occlusion); + this->enterLayer(parent, occlusion); + 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()); } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestFilters); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestReplicaDoesOcclude : 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, 100), IntSize(50, 50), true); + this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(50, 50), IntSize()); + this->calcDrawEtc(parent); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + this->visitLayer(surface, occlusion); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + EXPECT_EQ_RECT(IntRect(0, 100, 50, 50), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 50, 50), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + this->visitContributingSurface(surface, occlusion); + this->enterLayer(parent, occlusion); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + // The surface and replica should both be occluding the parent. + EXPECT_EQ_RECT(IntRect(0, 100, 100, 100), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; - // Opacity layer won't contribute to occlusion. - occlusion.enterTargetRenderSurface(opacityLayer->renderSurface()); - occlusion.markOccludedBehindLayer(opacityLayer.get()); - occlusion.finishedTargetRenderSurface(opacityLayer.get(), opacityLayer->renderSurface()); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaDoesOcclude); - EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); - EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestReplicaWithClipping : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 170)); + typename Types::LayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(50, 50), true); + this->createReplicaLayer(surface, this->identityMatrix, FloatPoint(50, 50), IntSize()); + this->calcDrawEtc(parent); - // And has nothing to contribute to its parent surface. - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - EXPECT_TRUE(occlusion.occlusionInScreenSpace().isEmpty()); - EXPECT_TRUE(occlusion.occlusionInTargetSurface().isEmpty()); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - // Opaque layer will contribute to occlusion. - occlusion.enterTargetRenderSurface(opaqueLayer->renderSurface()); - occlusion.markOccludedBehindLayer(opaqueLayer.get()); - occlusion.finishedTargetRenderSurface(opaqueLayer.get(), opaqueLayer->renderSurface()); + this->visitLayer(surface, occlusion); - 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()); + EXPECT_EQ_RECT(IntRect(0, 100, 50, 50), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 50, 50), 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()); + this->visitContributingSurface(surface, occlusion); + this->enterLayer(parent, occlusion); - // 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()); + // The surface and replica should both be occluding the parent. + EXPECT_EQ_RECT(IntRect(0, 100, 100, 70), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; - // 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()); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaWithClipping); - // 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()); -} +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLayerScissorRectOutsideChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(200, 100, 100, 100)); + + this->enterLayer(layer, occlusion); + + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(200, 100, 100, 100))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(200, 100, 100, 100))); + occlusion.setLayerScissorRect(IntRect(200, 100, 100, 100)); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_EQ_RECT(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + } +}; -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_occlusionInteractionWithFilters, occlusionInteractionWithFilters); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerScissorRectOutsideChild); -void layerScissorRectOverTile(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(200, 100, 100, 100)); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); - - occlusion.useDefaultLayerScissorRect(); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); - occlusion.setLayerScissorRect(IntRect(200, 100, 100, 100)); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_EQ_RECT(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300))); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerScissorRectOverTile, layerScissorRectOverTile); - -void screenScissorRectOverTile(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestScreenScissorRectOutsideChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(200, 100, 100, 100)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->enterLayer(layer, occlusion); + + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(200, 100, 100, 100))); + + occlusion.useDefaultLayerScissorRect(); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(200, 100, 100, 100))); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_EQ_RECT(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + } +}; - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestScreenScissorRectOutsideChild); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLayerScissorRectOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(100, 100, 100, 100)); + + this->enterLayer(layer, occlusion); + + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty()); + } +}; - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerScissorRectOverChild); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestScreenScissorRectOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(100, 100, 100, 100)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->enterLayer(layer, occlusion); + + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty()); + } +}; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestScreenScissorRectOverChild); - TestCCOcclusionTracker occlusion(IntRect(200, 100, 100, 100)); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLayerScissorRectPartlyOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(50, 50, 200, 200)); + + this->enterLayer(layer, occlusion); + + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + EXPECT_EQ_RECT(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100))); + EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100))); + EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100))); + EXPECT_EQ_RECT(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100))); + } +}; - occlusion.enterTargetRenderSurface(layer->renderSurface()); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerScissorRectPartlyOverChild); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestScreenScissorRectPartlyOverChild : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(50, 50, 200, 200)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->enterLayer(layer, occlusion); + + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + EXPECT_EQ_RECT(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100))); + EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100))); + EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100))); + EXPECT_EQ_RECT(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100))); + } +}; - // Occluded since its outside the surface bounds. - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestScreenScissorRectPartlyOverChild); - // Test without any scissors. - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); - occlusion.useDefaultLayerScissorRect(); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLayerScissorRectOverNothing : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(500, 500, 100, 100)); + + this->enterLayer(layer, occlusion); + + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100)).isEmpty()); + } +}; - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerScissorRectOverNothing); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestScreenScissorRectOverNothing : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(500, 500, 100, 100)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->enterLayer(layer, occlusion); + + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); + + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 0, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(0, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 200, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 100)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(0, 100, 300, 100)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(200, 100, 100, 100)).isEmpty()); + EXPECT_TRUE(occlusion.unoccludedContentRect(parent, IntRect(100, 200, 100, 100)).isEmpty()); + } +}; - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestScreenScissorRectOverNothing); - EXPECT_EQ_RECT(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300))); -} +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLayerScissorRectForLayerOffOrigin : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + this->enterLayer(layer, occlusion); + + // This layer is translated when drawn into its target. So if the scissor rect given from the target surface + // is not in that target space, then after translating these query rects into the target, they will fall outside + // the scissor and be considered occluded. + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); + } +}; -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_screenScissorRectOverTile, screenScissorRectOverTile); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestLayerScissorRectForLayerOffOrigin); -void layerScissorRectOverCulledTile(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestOpaqueContentsRegionEmpty : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 200), false); + this->calcDrawEtc(parent); - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + this->enterLayer(layer, occlusion); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 0, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(100, 100, 100, 100))); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + // Occluded since its outside the surface bounds. + EXPECT_TRUE(occlusion.occluded(layer, IntRect(200, 100, 100, 100))); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + // Test without any scissors. + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + EXPECT_FALSE(occlusion.occluded(layer, IntRect(200, 100, 100, 100))); + occlusion.useDefaultLayerScissorRect(); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + this->leaveLayer(layer, occlusion); + this->visitContributingSurface(layer, occlusion); + this->enterLayer(parent, occlusion); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(100, 100, 100, 100)); + EXPECT_TRUE(occlusion.occlusionInScreenSpace().bounds().isEmpty()); + EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size()); + } +}; - occlusion.enterTargetRenderSurface(layer->renderSurface()); +MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestOpaqueContentsRegionEmpty); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestOpaqueContentsRegionNonEmpty : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 100), IntSize(200, 200), false); + this->calcDrawEtc(parent); - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); + { + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + layer->setOpaqueContentsRect(IntRect(0, 0, 100, 100)); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); + this->resetLayerIterator(); + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + EXPECT_EQ_RECT(IntRect(100, 100, 100, 100), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300)).isEmpty()); -} + EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + } -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerScissorRectOverCulledTile, layerScissorRectOverCulledTile); + { + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + layer->setOpaqueContentsRect(IntRect(20, 20, 180, 180)); -void screenScissorRectOverCulledTile(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); + this->resetLayerIterator(); + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); + EXPECT_EQ_RECT(IntRect(120, 120, 180, 180), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_TRUE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + } - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + { + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + layer->setOpaqueContentsRect(IntRect(150, 150, 100, 100)); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + this->resetLayerIterator(); + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + EXPECT_EQ_RECT(IntRect(250, 250, 50, 50), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); - TestCCOcclusionTracker occlusion(IntRect(100, 100, 100, 100)); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(0, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(100, 100, 100, 100))); + EXPECT_FALSE(occlusion.occluded(parent, IntRect(200, 200, 100, 100))); + } + } +}; - occlusion.enterTargetRenderSurface(layer->renderSurface()); +MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestOpaqueContentsRegionNonEmpty); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTest3dTransform : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix transform; + transform.rotate3d(0, 30, 0); - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::LayerType* container = this->createLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(container, transform, FloatPoint(100, 100), IntSize(200, 200), true); + this->calcDrawEtc(parent); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + this->enterLayer(layer, occlusion); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + // The layer is rotated in 3d but without preserving 3d, so it only gets resized. + EXPECT_EQ_RECT(IntRect(0, 0, 200, 200), occlusion.unoccludedContentRect(layer, IntRect(0, 0, 200, 200))); + } +}; - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300)).isEmpty()); -} +MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTest3dTransform); -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_screenScissorRectOverCulledTile, screenScissorRectOverCulledTile); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestPerspectiveTransform : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix transform; + transform.translate(150, 150); + transform.applyPerspective(400); + transform.rotate3d(1, 0, 0, -30); + transform.translate(-150, -150); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::LayerType* container = this->createLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(container, transform, FloatPoint(100, 100), IntSize(200, 200), true); + container->setPreserves3D(true); + layer->setPreserves3D(true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + this->enterLayer(layer, occlusion); + + EXPECT_EQ_RECT(IntRect(0, 0, 200, 200), occlusion.unoccludedContentRect(layer, IntRect(0, 0, 200, 200))); + } +}; -void layerScissorRectOverPartialTiles(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(50, 50, 200, 200)); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300))); - EXPECT_EQ_RECT(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 100))); - EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 100, 300, 100))); - EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(200, 100, 100, 100))); - EXPECT_EQ_RECT(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(100, 200, 100, 100))); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerScissorRectOverPartialTiles, layerScissorRectOverPartialTiles); - -void screenScissorRectOverPartialTiles(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(50, 50, 200, 200)); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300))); - EXPECT_EQ_RECT(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 100))); - EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 100, 300, 100))); - EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(200, 100, 100, 100))); - EXPECT_EQ_RECT(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(100, 200, 100, 100))); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_screenScissorRectOverPartialTiles, screenScissorRectOverPartialTiles); - -void layerScissorRectOverNoTiles(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.setLayerScissorRect(IntRect(500, 500, 100, 100)); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 100, 300, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(200, 100, 100, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(100, 200, 100, 100)).isEmpty()); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerScissorRectOverNoTiles, layerScissorRectOverNoTiles); - -void screenScissorRectOverNoTiles(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestCCOcclusionTracker occlusion(IntRect(500, 500, 100, 100)); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 100, 300, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(200, 100, 100, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(100, 200, 100, 100)).isEmpty()); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_screenScissorRectOverNoTiles, screenScissorRectOverNoTiles); - -void layerScissorRectForLayerOffOrigin(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); +MAIN_THREAD_TEST(CCOcclusionTrackerTestPerspectiveTransform); - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestPerspectiveTransformBehindCamera : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + // This test is based on the platform/chromium/compositing/3d-corners.html layout test. + TransformationMatrix transform; + transform.translate(250, 50); + transform.applyPerspective(10); + transform.translate(-250, -50); + transform.translate(250, 50); + transform.rotate3d(1, 0, 0, -167); + transform.translate(-250, -50); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(500, 100)); + typename Types::LayerType* container = this->createLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(500, 500)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(container, transform, FloatPoint(0, 0), IntSize(500, 500), true); + container->setPreserves3D(true); + layer->setPreserves3D(true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + this->enterLayer(layer, occlusion); + + // The bottom 11 pixel rows of this layer remain visible inside the container, after translation to the target surface. When translated back, + // this will include many more pixels but must include at least the bottom 11 rows. + EXPECT_TRUE(occlusion.unoccludedContentRect(layer, IntRect(0, 0, 500, 500)).contains(IntRect(0, 489, 500, 11))); + } +}; - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(100, 100), IntSize(200, 200), true, opaqueLayers); +MAIN_THREAD_TEST(CCOcclusionTrackerTestPerspectiveTransformBehindCamera); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLayerBehindCameraDoesNotOcclude : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix transform; + transform.translate(50, 50); + transform.applyPerspective(100); + transform.translate3d(0, 0, 110); + transform.translate(-50, -50); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, transform, FloatPoint(0, 0), IntSize(100, 100), true); + parent->setPreserves3D(true); + layer->setPreserves3D(true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + + // The |layer| is entirely behind the camera and should not occlude. + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size()); + EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size()); + } +}; - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); +MAIN_THREAD_TEST(CCOcclusionTrackerTestLayerBehindCameraDoesNotOcclude); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestLargePixelsOccludeInsideClipRect : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix transform; + transform.translate(50, 50); + transform.applyPerspective(100); + transform.translate3d(0, 0, 99); + transform.translate(-50, -50); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(100, 100)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, transform, FloatPoint(0, 0), IntSize(100, 100), true); + parent->setPreserves3D(true); + layer->setPreserves3D(true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + + // This is very close to the camera, so pixels in its visibleLayerRect will actually go outside of the layer's clipRect. + // Ensure that those pixels don't occlude things outside the clipRect. + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + EXPECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + EXPECT_EQ(IntRect(0, 0, 100, 100), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + } +}; - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); +MAIN_THREAD_TEST(CCOcclusionTrackerTestLargePixelsOccludeInsideClipRect); - occlusion.enterTargetRenderSurface(layer->renderSurface()); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestAnimationOpacity1OnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true); + typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true); + typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true); + typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true); + typename Types::ContentLayerType* parent2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false); + 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); + this->calcDrawEtc(parent); + + EXPECT_TRUE(layer->drawOpacityIsAnimating()); + EXPECT_FALSE(surface->drawOpacityIsAnimating()); + EXPECT_TRUE(surface->renderSurface()->drawOpacityIsAnimating()); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + + this->visitLayer(topmost, occlusion); + this->enterLayer(parent2, occlusion); + // This occlusion will affect all surfaces. + EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent2, IntRect(0, 0, 300, 300))); + this->leaveLayer(parent2, occlusion); + + this->visitLayer(surfaceChild2, occlusion); + this->enterLayer(surfaceChild, occlusion); + EXPECT_EQ_RECT(IntRect(100, 0, 150, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 300, 300))); + this->leaveLayer(surfaceChild, occlusion); + this->enterLayer(surface, occlusion); + EXPECT_EQ_RECT(IntRect(200, 0, 50, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300))); + this->leaveLayer(surface, occlusion); + + this->enterContributingSurface(surface, occlusion); + // Occlusion within the surface is lost when leaving the animating surface. + EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), false, IntRect(0, 0, 300, 300))); + this->leaveContributingSurface(surface, occlusion); + + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + + // Occlusion is not added for the animating |layer|. + EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + } +}; - // This layer is translated when drawn into its target. So if the scissor rect given from the target surface - // is not in that target space, then after translating these query rects into the target, they will fall outside - // the scissor and be considered occluded. - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); -} +MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationOpacity1OnMainThread); -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_layerScissorRectForLayerOffOrigin, layerScissorRectForLayerOffOrigin); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestAnimationOpacity0OnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true); + typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true); + typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true); + typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true); + typename Types::ContentLayerType* parent2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false); + 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); + this->calcDrawEtc(parent); + + EXPECT_TRUE(layer->drawOpacityIsAnimating()); + EXPECT_FALSE(surface->drawOpacityIsAnimating()); + EXPECT_TRUE(surface->renderSurface()->drawOpacityIsAnimating()); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + + this->visitLayer(topmost, occlusion); + this->enterLayer(parent2, occlusion); + // This occlusion will affect all surfaces. + EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + this->leaveLayer(parent2, occlusion); + + this->visitLayer(surfaceChild2, occlusion); + this->enterLayer(surfaceChild, occlusion); + EXPECT_EQ_RECT(IntRect(100, 0, 150, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 300, 300))); + this->leaveLayer(surfaceChild, occlusion); + this->enterLayer(surface, occlusion); + EXPECT_EQ_RECT(IntRect(200, 0, 50, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300))); + this->leaveLayer(surface, occlusion); + + this->enterContributingSurface(surface, occlusion); + // Occlusion within the surface is lost when leaving the animating surface. + EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), false, IntRect(0, 0, 300, 300))); + this->leaveContributingSurface(surface, occlusion); + + this->visitLayer(layer, occlusion); + this->enterLayer(parent, occlusion); + + // Occlusion is not added for the animating |layer|. + EXPECT_EQ_RECT(IntRect(0, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + } +}; -void damageRectOverTile(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); +MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationOpacity0OnMainThread); - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestAnimationTranslateOnMainThread : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* layer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true); + typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300), true); + typename Types::ContentLayerType* surfaceChild = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(200, 300), true); + typename Types::ContentLayerType* surfaceChild2 = this->createDrawingLayer(surface, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 300), true); + typename Types::ContentLayerType* surface2 = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(50, 300), true); + + addAnimatedTransformToController(*layer->layerAnimationController(), 10, 30, 0); + addAnimatedTransformToController(*surface->layerAnimationController(), 10, 30, 0); + addAnimatedTransformToController(*surfaceChild->layerAnimationController(), 10, 30, 0); + this->calcDrawEtc(parent); + + EXPECT_TRUE(layer->drawTransformIsAnimating()); + EXPECT_TRUE(layer->screenSpaceTransformIsAnimating()); + EXPECT_TRUE(surface->renderSurface()->targetSurfaceTransformsAreAnimating()); + EXPECT_TRUE(surface->renderSurface()->screenSpaceTransformsAreAnimating()); + // The surface owning layer doesn't animate against its own surface. + EXPECT_FALSE(surface->drawTransformIsAnimating()); + EXPECT_TRUE(surface->screenSpaceTransformIsAnimating()); + EXPECT_TRUE(surfaceChild->drawTransformIsAnimating()); + EXPECT_TRUE(surfaceChild->screenSpaceTransformIsAnimating()); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + + this->visitLayer(surface2, occlusion); + this->enterContributingSurface(surface2, occlusion); + + EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); + this->leaveContributingSurface(surface2, occlusion); + this->enterLayer(surfaceChild2, occlusion); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + // surfaceChild2 is moving in screen space but not relative to its target, so occlusion should happen in its target space only. + // It also means that things occluding in screen space (e.g. surface2) cannot occlude this layer. + EXPECT_EQ_RECT(IntRect(0, 0, 100, 300), occlusion.unoccludedContentRect(surfaceChild2, IntRect(0, 0, 100, 300))); + EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 50, 300))); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + this->leaveLayer(surfaceChild2, occlusion); + this->enterLayer(surfaceChild, occlusion); + EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 100, 300))); + EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 100, 300), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300))); + + // The surfaceChild is occluded by the surfaceChild2, but is moving relative its target and the screen, so it + // can't be occluded. + EXPECT_EQ_RECT(IntRect(0, 0, 200, 300), occlusion.unoccludedContentRect(surfaceChild, IntRect(0, 0, 200, 300))); + EXPECT_FALSE(occlusion.occluded(surfaceChild, IntRect(0, 0, 50, 300))); + + this->leaveLayer(surfaceChild, occlusion); + this->enterLayer(surface, occlusion); + // The surfaceChild is moving in screen space but not relative to its target, so occlusion should happen in its target space only. + EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 100, 300), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + EXPECT_EQ_RECT(IntRect(100, 0, 200, 300), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300))); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + this->leaveLayer(surface, occlusion); + // The surface's owning layer is moving in screen space but not relative to its target, so occlusion should happen in its target space only. + EXPECT_EQ_RECT(IntRect(0, 0, 50, 300), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContentRect(surface, IntRect(0, 0, 300, 300))); + + this->enterContributingSurface(surface, occlusion); + // The contributing |surface| is animating so it can't be occluded. + EXPECT_EQ_RECT(IntRect(0, 0, 300, 300), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), false, IntRect(0, 0, 300, 300))); + this->leaveContributingSurface(surface, occlusion); + + this->enterLayer(layer, occlusion); + // 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))); + this->leaveLayer(layer, occlusion); + + this->enterLayer(parent, occlusion); + // The |layer| is animating in the screen and in its target, so no occlusion is added. + EXPECT_EQ_RECT(IntRect(50, 0, 250, 300), occlusion.unoccludedContentRect(parent, IntRect(0, 0, 300, 300))); + } +}; - TestDamageClient damage(FloatRect(200, 100, 100, 100)); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000), &damage); +MAIN_THREAD_TEST(CCOcclusionTrackerTestAnimationTranslateOnMainThread); - occlusion.enterTargetRenderSurface(layer->renderSurface()); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestSurfaceOcclusionTranslatesToParent : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix surfaceTransform; + surfaceTransform.translate(300, 300); + surfaceTransform.scale(2); + surfaceTransform.translate(-150, -150); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(500, 500)); + typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, surfaceTransform, FloatPoint(0, 0), IntSize(300, 300), false); + typename Types::ContentLayerType* surface2 = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(50, 50), IntSize(300, 300), false); + surface->setOpaqueContentsRect(IntRect(0, 0, 200, 200)); + surface2->setOpaqueContentsRect(IntRect(0, 0, 200, 200)); + this->calcDrawEtc(parent); - // Outside the layer's clip rect. - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); + this->visitLayer(surface2, occlusion); + this->visitContributingSurface(surface2, occlusion); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); + EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + // Clear any stored occlusion. + occlusion.setOcclusionInScreenSpace(Region()); + occlusion.setOcclusionInTargetSurface(Region()); - EXPECT_EQ_RECT(IntRect(200, 100, 100, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300))); -} + this->visitLayer(surface, occlusion); + this->visitContributingSurface(surface, occlusion); -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_damageRectOverTile, damageRectOverTile); + EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; -void damageRectOverCulledTile(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); +MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestSurfaceOcclusionTranslatesToParent); - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); + typename Types::ContentLayerType* surface = this->createDrawingSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(500, 300), false); + surface->setOpaqueContentsRect(IntRect(0, 0, 400, 200)); + this->calcDrawEtc(parent); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + this->visitLayer(surface, occlusion); + this->visitContributingSurface(surface, occlusion); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 200), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 200), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); +MAIN_AND_IMPL_THREAD_TEST(CCOcclusionTrackerTestSurfaceOcclusionTranslatesWithClipping); - TestDamageClient damage(FloatRect(100, 100, 100, 100)); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000), &damage); +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); - occlusion.enterTargetRenderSurface(layer->renderSurface()); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); + // |topmost| occludes the replica, but not the surface itself. + this->visitLayer(topmost, occlusion); - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); + 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()); + + this->visitLayer(surface, occlusion); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); + 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.leaveToTargetRenderSurface(parent->renderSurface()); + this->enterContributingSurface(surface, occlusion); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300)).isEmpty()); -} + // Surface is not occluded so it shouldn't think it is. + EXPECT_EQ_RECT(IntRect(0, 0, 100, 100), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), false, IntRect(0, 0, 100, 100))); + } +}; -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_damageRectOverCulledTile, damageRectOverCulledTile); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReplicaOccluded); -void damageRectOverPartialTiles(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestDamageClient damage(FloatRect(50, 50, 200, 200)); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000), &damage); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_EQ_RECT(IntRect(50, 50, 200, 200), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300))); - EXPECT_EQ_RECT(IntRect(200, 50, 50, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 100))); - EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(0, 100, 300, 100))); - EXPECT_EQ_RECT(IntRect(200, 100, 50, 100), occlusion.unoccludedContentRect(parent.get(), IntRect(200, 100, 100, 100))); - EXPECT_EQ_RECT(IntRect(100, 200, 100, 50), occlusion.unoccludedContentRect(parent.get(), IntRect(100, 200, 100, 100))); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_damageRectOverPartialTiles, damageRectOverPartialTiles); - -void damageRectOverNoTiles(bool opaqueLayers) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> parentLayer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(parentLayer); - parent->addChild(layer); - - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - parentLayer->setFilters(filters); - layer->setFilters(filters); - - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(parentLayer.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300), false, opaqueLayers); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), true, opaqueLayers); - - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > 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); - - TestDamageClient damage(FloatRect(500, 500, 100, 100)); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000), &damage); - - occlusion.enterTargetRenderSurface(layer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); - - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - occlusion.enterTargetRenderSurface(parentLayer->renderSurface()); - - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 0, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(0, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(100, 200, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parentLayer.get(), IntRect(200, 200, 100, 100))); - - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); - - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 300)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 0, 300, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(0, 100, 300, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(200, 100, 100, 100)).isEmpty()); - EXPECT_TRUE(occlusion.unoccludedContentRect(parent.get(), IntRect(100, 200, 100, 100)).isEmpty()); -} - -TEST_OPAQUE_AND_PAINTED_OPAQUE(CCOcclusionTrackerTest_damageRectOverNoTiles, damageRectOverNoTiles); - -TEST(CCOcclusionTrackerTest, opaqueContentsRegionEmpty) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); +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); - FilterOperations filters; - filters.operations().append(BasicComponentTransferFilterOperation::create(0.5, FilterOperation::GRAYSCALE)); - layer->setFilters(filters); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(0, 0), IntSize(200, 200), false, false); + // |topmost| occludes the surface, but not the entire surface's replica. + this->visitLayer(topmost, occlusion); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + 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()); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + this->visitLayer(surface, occlusion); - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); + 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()); - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); + this->enterContributingSurface(surface, occlusion); - occlusion.enterTargetRenderSurface(layer->renderSurface()); + // Surface is occluded, but only the top 10px of the replica. + EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), false, IntRect(0, 0, 100, 100))); + EXPECT_EQ_RECT(IntRect(0, 10, 100, 90), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), true, IntRect(0, 0, 100, 100))); + } +}; - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 0, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(100, 100, 100, 100))); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceWithReplicaUnoccluded); - // Occluded since its outside the surface bounds. - EXPECT_TRUE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); +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); - // Test without any scissors. - occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - EXPECT_FALSE(occlusion.occluded(layer.get(), IntRect(200, 100, 100, 100))); - occlusion.useDefaultLayerScissorRect(); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); - occlusion.markOccludedBehindLayer(layer.get()); - occlusion.leaveToTargetRenderSurface(parent->renderSurface()); + // These occlude the surface and replica differently, so we can test each one. + this->visitLayer(overReplica, occlusion); + this->visitLayer(overSurface, occlusion); - EXPECT_TRUE(occlusion.occlusionInScreenSpace().bounds().isEmpty()); - EXPECT_EQ(0u, occlusion.occlusionInScreenSpace().rects().size()); -} + 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()); -TEST(CCOcclusionTrackerTest, opaqueContentsRegionNonEmpty) -{ - const TransformationMatrix identityMatrix; - RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<LayerChromiumWithForcedDrawsContent> layer = adoptRef(new LayerChromiumWithForcedDrawsContent()); - parent->createRenderSurface(); - parent->addChild(layer); + this->visitLayer(surface, occlusion); - setLayerPropertiesForTesting(parent.get(), identityMatrix, FloatPoint(0, 0), IntSize(300, 300)); - setLayerPropertiesForTesting(layer.get(), identityMatrix, FloatPoint(100, 100), IntSize(200, 200), false, false); + 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()); - Vector<RefPtr<LayerChromium> > renderSurfaceLayerList; - Vector<RefPtr<LayerChromium> > dummyLayerList; - int dummyMaxTextureSize = 512; + this->enterContributingSurface(surface, occlusion); - parent->renderSurface()->setContentRect(IntRect(IntPoint::zero(), parent->bounds())); - parent->setClipRect(IntRect(IntPoint::zero(), parent->bounds())); - renderSurfaceLayerList.append(parent); + // Surface and replica are occluded different amounts. + EXPECT_EQ_RECT(IntRect(40, 0, 60, 100), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), false, IntRect(0, 0, 100, 100))); + EXPECT_EQ_RECT(IntRect(50, 0, 50, 100), occlusion.unoccludedContributingSurfaceContentRect(surface->renderSurface(), true, IntRect(0, 0, 100, 100))); + } +}; - CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(parent.get(), parent.get(), identityMatrix, identityMatrix, renderSurfaceLayerList, dummyLayerList, dummyMaxTextureSize); +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceAndReplicaOccludedDifferently); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestSurfaceChildOfSurface : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() { - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - layer->setOpaquePaintRect(IntRect(0, 0, 100, 100)); + // 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, 10), IntSize(100, 50), true); + typename Types::LayerType* topmost = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(100, 50), true); + this->calcDrawEtc(parent); - occlusion.enterTargetRenderSurface(parent->renderSurface()); - occlusion.markOccludedBehindLayer(layer.get()); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(-100, -100, 1000, 1000)); - EXPECT_EQ_RECT(IntRect(100, 100, 100, 100), occlusion.occlusionInScreenSpace().bounds()); + // |topmost| occludes everything partially so we know occlusion is happening at all. + this->visitLayer(topmost, occlusion); + + 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()); + + this->visitLayer(surfaceChild, occlusion); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(0, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(200, 200, 100, 100))); + // surfaceChild increases the occlusion in the screen by a narrow sliver. + EXPECT_EQ_RECT(IntRect(0, 0, 100, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + // In its own surface, surfaceChild is at 0,0 as is its occlusion. + EXPECT_EQ_RECT(IntRect(0, 0, 100, 50), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, 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. + + this->enterContributingSurface(surfaceChild, occlusion); + // The surfaceChild's parent does not have a clipRect as it owns a render surface. Make sure the unoccluded rect + // does not get clipped away inappropriately. + EXPECT_EQ_RECT(IntRect(0, 40, 100, 10), occlusion.unoccludedContributingSurfaceContentRect(surfaceChild->renderSurface(), false, IntRect(0, 0, 100, 50))); + this->leaveContributingSurface(surfaceChild, occlusion); + + // When the surfaceChild's occlusion is transformed up to its parent, make sure it is not clipped away inappropriately also. + this->enterLayer(surface, occlusion); + EXPECT_EQ_RECT(IntRect(0, 0, 100, 60), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 10, 100, 50), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + this->leaveLayer(surface, occlusion); + + this->enterContributingSurface(surface, occlusion); + // 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->renderSurface(), false, IntRect(0, 0, 100, 100))); } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceChildOfSurface); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestSurfaceChildOfClippingSurface : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() { - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - layer->setOpaquePaintRect(IntRect(20, 20, 180, 180)); + // 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)); - occlusion.enterTargetRenderSurface(parent->renderSurface()); - occlusion.markOccludedBehindLayer(layer.get()); + // |topmost| occludes everything partially so we know occlusion is happening at all. + this->visitLayer(topmost, occlusion); - EXPECT_EQ_RECT(IntRect(120, 120, 180, 180), occlusion.occlusionInScreenSpace().bounds()); + 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()); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(100, 100, 100, 100))); - EXPECT_TRUE(occlusion.occluded(parent.get(), IntRect(200, 200, 100, 100))); + // surfaceChild is not opaque and does not occlude, so we have a non-empty unoccluded area on surface. + this->visitLayer(surfaceChild, occlusion); + + 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. + + this->enterContributingSurface(surfaceChild, occlusion); + // 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->renderSurface(), false, IntRect(0, 0, 100, 100))); + this->leaveContributingSurface(surfaceChild, occlusion); + + this->visitLayer(surface, occlusion); + this->enterContributingSurface(surface, occlusion); + // 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->renderSurface(), false, IntRect(0, 0, 100, 100))); } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestSurfaceChildOfClippingSurface); +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilter : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() { - TestCCOcclusionTracker occlusion(IntRect(0, 0, 1000, 1000)); - layer->setOpaquePaintRect(IntRect(150, 150, 100, 100)); + TransformationMatrix scaleByHalf; + scaleByHalf.scale(0.5); + + // Make a surface and its replica, each 50x50, that are completely surrounded by opaque layers which are above them in the z-order. + // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface + // appears at 50, 50 and the replica at 200, 50. + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150)); + typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false); + this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize()); + typename Types::LayerType* occludingLayer1 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 50), true); + typename Types::LayerType* occludingLayer2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(300, 50), true); + typename Types::LayerType* occludingLayer3 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 50), IntSize(50, 50), true); + typename Types::LayerType* occludingLayer4 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 50), IntSize(100, 50), true); + typename Types::LayerType* occludingLayer5 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 50), IntSize(50, 50), true); + + // Filters make the layer own a surface. + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(10, WebCore::Fixed), FilterOperation::BLUR)); + filteredSurface->setBackgroundFilters(filters); + + // Save the distance of influence for the blur effect. + int outsetTop, outsetRight, outsetBottom, outsetLeft; + filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); + + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + // These layers occlude pixels directly beside the filteredSurface. Because filtered surface blends pixels in a radius, it will + // need to see some of the pixels (up to radius far) underneath the occludingLayers. + this->visitLayer(occludingLayer5, occlusion); + this->visitLayer(occludingLayer4, occlusion); + this->visitLayer(occludingLayer3, occlusion); + this->visitLayer(occludingLayer2, occlusion); + this->visitLayer(occludingLayer1, occlusion); + + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInTargetSurface().rects().size()); + + // Everything outside the surface/replica is occluded but the surface/replica itself is not. + this->enterLayer(filteredSurface, occlusion); + EXPECT_EQ_RECT(IntRect(1, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(1, 0, 100, 100))); + EXPECT_EQ_RECT(IntRect(0, 1, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, 1, 100, 100))); + EXPECT_EQ_RECT(IntRect(0, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(-1, 0, 100, 100))); + EXPECT_EQ_RECT(IntRect(0, 0, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, -1, 100, 100))); + + EXPECT_EQ_RECT(IntRect(300 + 1, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 + 1, 0, 100, 100))); + EXPECT_EQ_RECT(IntRect(300 + 0, 1, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 + 0, 1, 100, 100))); + EXPECT_EQ_RECT(IntRect(300 + 0, 0, 99, 100), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 - 1, 0, 100, 100))); + EXPECT_EQ_RECT(IntRect(300 + 0, 0, 100, 99), occlusion.unoccludedContentRect(filteredSurface, IntRect(300 + 0, -1, 100, 100))); + this->leaveLayer(filteredSurface, occlusion); + + // The filtered layer/replica does not occlude. + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size()); + + // The surface has a background blur, so it needs pixels that are currently considered occluded in order to be drawn. So the pixels + // it needs should be removed some the occluded area so that when we get to the parent they are drawn. + this->visitContributingSurface(filteredSurface, occlusion); + + this->enterLayer(parent, occlusion); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInTargetSurface().rects().size()); + + IntRect outsetRect; + IntRect testRect; + + // Nothing in the blur outsets for the filteredSurface is occluded. + outsetRect = IntRect(50 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom); + testRect = outsetRect; + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + + // Stuff outside the blur outsets is still occluded though. + testRect = outsetRect; + testRect.expand(1, 0); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + testRect = outsetRect; + testRect.expand(0, 1); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + testRect = outsetRect; + testRect.move(-1, 0); + testRect.expand(1, 0); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + testRect = outsetRect; + testRect.move(0, -1); + testRect.expand(0, 1); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + + // Nothing in the blur outsets for the filteredSurface's replica is occluded. + outsetRect = IntRect(200 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom); + testRect = outsetRect; + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + + // Stuff outside the blur outsets is still occluded though. + testRect = outsetRect; + testRect.expand(1, 0); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + testRect = outsetRect; + testRect.expand(0, 1); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + testRect = outsetRect; + testRect.move(-1, 0); + testRect.expand(1, 0); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + testRect = outsetRect; + testRect.move(0, -1); + testRect.expand(0, 1); + EXPECT_EQ_RECT(outsetRect, occlusion.unoccludedContentRect(parent, testRect)); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilter); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestTwoBackgroundFiltersReduceOcclusionTwice : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix scaleByHalf; + scaleByHalf.scale(0.5); + + // Makes two surfaces that completely cover |parent|. The occlusion both above and below the filters will be reduced by each of them. + typename Types::ContentLayerType* root = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(75, 75)); + typename Types::LayerType* parent = this->createSurface(root, scaleByHalf, FloatPoint(0, 0), IntSize(150, 150)); + parent->setMasksToBounds(true); + typename Types::LayerType* filteredSurface1 = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(0, 0), IntSize(300, 300), false); + typename Types::LayerType* filteredSurface2 = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(0, 0), IntSize(300, 300), false); + typename Types::LayerType* occludingLayerAbove = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 100), IntSize(50, 50), true); + + // Filters make the layers own surfaces. + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(3, WebCore::Fixed), FilterOperation::BLUR)); + filteredSurface1->setBackgroundFilters(filters); + filteredSurface2->setBackgroundFilters(filters); + + // Save the distance of influence for the blur effect. + int outsetTop, outsetRight, outsetBottom, outsetLeft; + filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); - occlusion.enterTargetRenderSurface(parent->renderSurface()); - occlusion.markOccludedBehindLayer(layer.get()); + this->calcDrawEtc(root); - EXPECT_EQ_RECT(IntRect(250, 250, 50, 50), occlusion.occlusionInScreenSpace().bounds()); + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->visitLayer(occludingLayerAbove, occlusion); + EXPECT_EQ_RECT(IntRect(100 / 2, 100 / 2, 50 / 2, 50 / 2), occlusion.occlusionInScreenSpace().bounds()); EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(100, 100, 50, 50), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + + this->visitLayer(filteredSurface2, occlusion); + this->visitContributingSurface(filteredSurface2, occlusion); + this->visitLayer(filteredSurface1, occlusion); + this->visitContributingSurface(filteredSurface1, occlusion); + + ASSERT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + ASSERT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(0, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(100, 100, 100, 100))); - EXPECT_FALSE(occlusion.occluded(parent.get(), IntRect(200, 200, 100, 100))); + // Test expectations in the target. + IntRect expectedOcclusion = IntRect(100 + outsetRight * 2, 100 + outsetBottom * 2, 50 - (outsetLeft + outsetRight) * 2, 50 - (outsetTop + outsetBottom) * 2); + EXPECT_EQ_RECT(expectedOcclusion, occlusion.occlusionInTargetSurface().rects()[0]); + + // Test expectations in the screen. Take the ceiling of half of the outsets. + outsetTop = (outsetTop + 1) / 2; + outsetRight = (outsetRight + 1) / 2; + outsetBottom = (outsetBottom + 1) / 2; + outsetLeft = (outsetLeft + 1) / 2; + expectedOcclusion = IntRect(100 / 2 + outsetRight * 2, 100 / 2 + outsetBottom * 2, 50 / 2 - (outsetLeft + outsetRight) * 2, 50 /2 - (outsetTop + outsetBottom) * 2); + + EXPECT_EQ_RECT(expectedOcclusion, occlusion.occlusionInScreenSpace().rects()[0]); } -} +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestTwoBackgroundFiltersReduceOcclusionTwice); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilterWithClip : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + // Make a surface and its replica, each 50x50, that are completely surrounded by opaque layers which are above them in the z-order. + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150)); + // We stick the filtered surface inside a clipping surface so that we can make sure the clip is honored when exposing pixels for + // the background filter. + typename Types::LayerType* clippingSurface = this->createSurface(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 70)); + clippingSurface->setMasksToBounds(true); + typename Types::LayerType* filteredSurface = this->createDrawingLayer(clippingSurface, this->identityMatrix, FloatPoint(50, 50), IntSize(50, 50), false); + this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(150, 0), IntSize()); + typename Types::LayerType* occludingLayer1 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), IntSize(300, 50), true); + typename Types::LayerType* occludingLayer2 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 100), IntSize(300, 50), true); + typename Types::LayerType* occludingLayer3 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 50), IntSize(50, 50), true); + typename Types::LayerType* occludingLayer4 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(100, 50), IntSize(100, 50), true); + typename Types::LayerType* occludingLayer5 = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(250, 50), IntSize(50, 50), true); + + // Filters make the layer own a surface. This filter is large enough that it goes outside the bottom of the clippingSurface. + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(12, WebCore::Fixed), FilterOperation::BLUR)); + filteredSurface->setBackgroundFilters(filters); + + // Save the distance of influence for the blur effect. + int outsetTop, outsetRight, outsetBottom, outsetLeft; + filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); + + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + // These layers occlude pixels directly beside the filteredSurface. Because filtered surface blends pixels in a radius, it will + // need to see some of the pixels (up to radius far) underneath the occludingLayers. + this->visitLayer(occludingLayer5, occlusion); + this->visitLayer(occludingLayer4, occlusion); + this->visitLayer(occludingLayer3, occlusion); + this->visitLayer(occludingLayer2, occlusion); + this->visitLayer(occludingLayer1, occlusion); + + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInTargetSurface().rects().size()); + + // Everything outside the surface/replica is occluded but the surface/replica itself is not. + this->enterLayer(filteredSurface, occlusion); + EXPECT_EQ_RECT(IntRect(1, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(1, 0, 50, 50))); + EXPECT_EQ_RECT(IntRect(0, 1, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, 1, 50, 50))); + EXPECT_EQ_RECT(IntRect(0, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(-1, 0, 50, 50))); + EXPECT_EQ_RECT(IntRect(0, 0, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(0, -1, 50, 50))); + + EXPECT_EQ_RECT(IntRect(150 + 1, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 + 1, 0, 50, 50))); + EXPECT_EQ_RECT(IntRect(150 + 0, 1, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 + 0, 1, 50, 50))); + EXPECT_EQ_RECT(IntRect(150 + 0, 0, 49, 50), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 - 1, 0, 50, 50))); + EXPECT_EQ_RECT(IntRect(150 + 0, 0, 50, 49), occlusion.unoccludedContentRect(filteredSurface, IntRect(150 + 0, -1, 50, 50))); + this->leaveLayer(filteredSurface, occlusion); + + // The filtered layer/replica does not occlude. + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(0u, occlusion.occlusionInTargetSurface().rects().size()); + + // The surface has a background blur, so it needs pixels that are currently considered occluded in order to be drawn. So the pixels + // it needs should be removed some the occluded area so that when we get to the parent they are drawn. + this->visitContributingSurface(filteredSurface, occlusion); + + this->enterContributingSurface(clippingSurface, occlusion); + EXPECT_EQ_RECT(IntRect(0, 0, 300, 150), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(5u, occlusion.occlusionInScreenSpace().rects().size()); + + IntRect outsetRect; + IntRect clippedOutsetRect; + IntRect testRect; + + // Nothing in the (clipped) blur outsets for the filteredSurface is occluded. + outsetRect = IntRect(50 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom); + clippedOutsetRect = intersection(outsetRect, IntRect(0 - outsetLeft, 0 - outsetTop, 300 + outsetLeft + outsetRight, 70 + outsetTop + outsetBottom)); + testRect = outsetRect; + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + + // Stuff outside the (clipped) blur outsets is still occluded though. + testRect = outsetRect; + testRect.expand(1, 0); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + testRect = outsetRect; + testRect.expand(0, 1); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + testRect = outsetRect; + testRect.move(-1, 0); + testRect.expand(1, 0); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + testRect = outsetRect; + testRect.move(0, -1); + testRect.expand(0, 1); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + + // Nothing in the (clipped) blur outsets for the filteredSurface's replica is occluded. + outsetRect = IntRect(200 - outsetLeft, 50 - outsetTop, 50 + outsetLeft + outsetRight, 50 + outsetTop + outsetBottom); + clippedOutsetRect = intersection(outsetRect, IntRect(0 - outsetLeft, 0 - outsetTop, 300 + outsetLeft + outsetRight, 70 + outsetTop + outsetBottom)); + testRect = outsetRect; + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + + // Stuff outside the (clipped) blur outsets is still occluded though. + testRect = outsetRect; + testRect.expand(1, 0); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + testRect = outsetRect; + testRect.expand(0, 1); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + testRect = outsetRect; + testRect.move(-1, 0); + testRect.expand(1, 0); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + testRect = outsetRect; + testRect.move(0, -1); + testRect.expand(0, 1); + EXPECT_EQ_RECT(clippedOutsetRect, occlusion.unoccludedContentRect(clippingSurface, testRect)); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontOccludePixelsNeededForBackgroundFilterWithClip); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestDontReduceOcclusionBelowBackgroundFilter : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix scaleByHalf; + scaleByHalf.scale(0.5); + + // Make a surface and its replica, each 50x50, with a smaller 30x30 layer centered below each. + // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface + // appears at 50, 50 and the replica at 200, 50. + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150)); + typename Types::LayerType* behindSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(60, 60), IntSize(30, 30), true); + typename Types::LayerType* behindReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(210, 60), IntSize(30, 30), true); + typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false); + this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize()); + + // Filters make the layer own a surface. + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(3, WebCore::Fixed), FilterOperation::BLUR)); + filteredSurface->setBackgroundFilters(filters); + + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + // The surface has a background blur, so it blurs non-opaque pixels below it. + this->visitLayer(filteredSurface, occlusion); + this->visitContributingSurface(filteredSurface, occlusion); + + this->visitLayer(behindReplicaLayer, occlusion); + this->visitLayer(behindSurfaceLayer, occlusion); + + // The layers behind the surface are not blurred, and their occlusion does not change, until we leave the surface. + // So it should not be modified by the filter here. + IntRect occlusionBehindSurface = IntRect(60, 60, 30, 30); + IntRect occlusionBehindReplica = IntRect(210, 60, 30, 30); + + IntRect expectedOpaqueBounds = unionRect(occlusionBehindSurface, occlusionBehindReplica); + EXPECT_EQ_RECT(expectedOpaqueBounds, occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(expectedOpaqueBounds, occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontReduceOcclusionBelowBackgroundFilter); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestDontReduceOcclusionIfBackgroundFilterIsOccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix scaleByHalf; + scaleByHalf.scale(0.5); + + // Make a surface and its replica, each 50x50, that are completely occluded by opaque layers which are above them in the z-order. + // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface + // appears at 50, 50 and the replica at 200, 50. + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150)); + typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false); + this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize()); + typename Types::LayerType* aboveSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(50, 50), IntSize(50, 50), true); + typename Types::LayerType* aboveReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(200, 50), IntSize(50, 50), true); + + // Filters make the layer own a surface. + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(3, WebCore::Fixed), FilterOperation::BLUR)); + filteredSurface->setBackgroundFilters(filters); + + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->visitLayer(aboveReplicaLayer, occlusion); + this->visitLayer(aboveSurfaceLayer, occlusion); + + // The surface has a background blur, so it blurs non-opaque pixels below it. + this->visitLayer(filteredSurface, occlusion); + this->visitContributingSurface(filteredSurface, occlusion); + + // The filter is completely occluded, so it should not blur anything and reduce any occlusion. + IntRect occlusionAboveSurface = IntRect(50, 50, 50, 50); + IntRect occlusionAboveReplica = IntRect(200, 50, 50, 50); + + IntRect expectedOpaqueBounds = unionRect(occlusionAboveSurface, occlusionAboveReplica); + EXPECT_EQ_RECT(expectedOpaqueBounds, occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(expectedOpaqueBounds, occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(2u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestDontReduceOcclusionIfBackgroundFilterIsOccluded); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestReduceOcclusionWhenBackgroundFilterIsPartiallyOccluded : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + TransformationMatrix scaleByHalf; + scaleByHalf.scale(0.5); + + // Make a surface and its replica, each 50x50, that are partially occluded by opaque layers which are above them in the z-order. + // The surface is scaled to test that the pixel moving is done in the target space, where the background filter is applied, but the surface + // appears at 50, 50 and the replica at 200, 50. + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(300, 150)); + typename Types::LayerType* filteredSurface = this->createDrawingLayer(parent, scaleByHalf, FloatPoint(50, 50), IntSize(100, 100), false); + this->createReplicaLayer(filteredSurface, this->identityMatrix, FloatPoint(300, 0), IntSize()); + typename Types::LayerType* aboveSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(70, 50), IntSize(30, 50), true); + typename Types::LayerType* aboveReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(200, 50), IntSize(30, 50), true); + typename Types::LayerType* besideSurfaceLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(90, 40), IntSize(10, 10), true); + typename Types::LayerType* besideReplicaLayer = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(200, 40), IntSize(10, 10), true); + + // Filters make the layer own a surface. + FilterOperations filters; + filters.operations().append(BlurFilterOperation::create(Length(3, WebCore::Fixed), FilterOperation::BLUR)); + filteredSurface->setBackgroundFilters(filters); + + // Save the distance of influence for the blur effect. + int outsetTop, outsetRight, outsetBottom, outsetLeft; + filters.getOutsets(outsetTop, outsetRight, outsetBottom, outsetLeft); + + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + + this->visitLayer(besideReplicaLayer, occlusion); + this->visitLayer(besideSurfaceLayer, occlusion); + this->visitLayer(aboveReplicaLayer, occlusion); + this->visitLayer(aboveSurfaceLayer, occlusion); + + // The surface has a background blur, so it blurs non-opaque pixels below it. + this->visitLayer(filteredSurface, occlusion); + this->visitContributingSurface(filteredSurface, occlusion); + + // The filter in the surface and replica are partially unoccluded. Only the unoccluded parts should reduce occlusion. + // This means it will push back the occlusion that touches the unoccluded part (occlusionAbove___), but it will not + // touch occlusionBeside____ since that is not beside the unoccluded part of the surface, even though it is beside + // the occluded part of the surface. + IntRect occlusionAboveSurface = IntRect(70 + outsetRight, 50, 30 - outsetRight, 50); + IntRect occlusionAboveReplica = IntRect(200, 50, 30 - outsetLeft, 50); + IntRect occlusionBesideSurface = IntRect(90, 40, 10, 10); + IntRect occlusionBesideReplica = IntRect(200, 40, 10, 10); + + Region expectedOcclusion; + expectedOcclusion.unite(occlusionAboveSurface); + expectedOcclusion.unite(occlusionAboveReplica); + expectedOcclusion.unite(occlusionBesideSurface); + expectedOcclusion.unite(occlusionBesideReplica); + + ASSERT_EQ(expectedOcclusion.rects().size(), occlusion.occlusionInTargetSurface().rects().size()); + ASSERT_EQ(expectedOcclusion.rects().size(), occlusion.occlusionInScreenSpace().rects().size()); + + for (size_t i = 0; i < expectedOcclusion.rects().size(); ++i) { + IntRect expectedRect = expectedOcclusion.rects()[i]; + IntRect screenRect = occlusion.occlusionInScreenSpace().rects()[i]; + IntRect targetRect = occlusion.occlusionInTargetSurface().rects()[i]; + EXPECT_EQ(expectedRect, screenRect); + EXPECT_EQ(expectedRect, targetRect); + } + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestReduceOcclusionWhenBackgroundFilterIsPartiallyOccluded); + +template<class Types, bool opaqueLayers> +class CCOcclusionTrackerTestMinimumTrackingSize : public CCOcclusionTrackerTest<Types, opaqueLayers> { +protected: + void runMyTest() + { + IntSize trackingSize(100, 100); + IntSize belowTrackingSize(99, 99); + + typename Types::ContentLayerType* parent = this->createRoot(this->identityMatrix, FloatPoint(0, 0), IntSize(400, 400)); + typename Types::LayerType* large = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), trackingSize, true); + typename Types::LayerType* small = this->createDrawingLayer(parent, this->identityMatrix, FloatPoint(0, 0), belowTrackingSize, true); + this->calcDrawEtc(parent); + + TestCCOcclusionTrackerWithScissor<typename Types::LayerType, typename Types::RenderSurfaceType> occlusion(IntRect(0, 0, 1000, 1000)); + occlusion.setLayerScissorRect(IntRect(0, 0, 1000, 1000)); + occlusion.setMinimumTrackingSize(trackingSize); + + // The small layer is not tracked because it is too small. + this->visitLayer(small, occlusion); + + 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()); + + // The large layer is tracked as it is large enough. + this->visitLayer(large, occlusion); + + EXPECT_EQ_RECT(IntRect(IntPoint(), trackingSize), occlusion.occlusionInScreenSpace().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInScreenSpace().rects().size()); + EXPECT_EQ_RECT(IntRect(IntPoint(), trackingSize), occlusion.occlusionInTargetSurface().bounds()); + EXPECT_EQ(1u, occlusion.occlusionInTargetSurface().rects().size()); + } +}; + +ALL_CCOCCLUSIONTRACKER_TEST(CCOcclusionTrackerTestMinimumTrackingSize); } // namespace diff --git a/Source/WebKit/chromium/tests/CCOcclusionTrackerTestCommon.h b/Source/WebKit/chromium/tests/CCOcclusionTrackerTestCommon.h new file mode 100644 index 000000000..fcdc3ed6c --- /dev/null +++ b/Source/WebKit/chromium/tests/CCOcclusionTrackerTestCommon.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef CCOcclusionTrackerTestCommon_h +#define CCOcclusionTrackerTestCommon_h + +#include "IntRect.h" +#include "Region.h" +#include "RenderSurfaceChromium.h" +#include "cc/CCOcclusionTracker.h" +#include "cc/CCRenderSurface.h" + +namespace WebKitTests { + +// A subclass to expose the total current occlusion. +template<typename LayerType, typename RenderSurfaceType> +class TestCCOcclusionTrackerBase : public WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType> { +public: + TestCCOcclusionTrackerBase(WebCore::IntRect screenScissorRect, bool recordMetricsForFrame = false) + : WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>(screenScissorRect, recordMetricsForFrame) + { + } + + WebCore::Region occlusionInScreenSpace() const { return WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInScreen; } + WebCore::Region occlusionInTargetSurface() const { return WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInTarget; } + + void setOcclusionInScreenSpace(const WebCore::Region& region) { WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInScreen = region; } + void setOcclusionInTargetSurface(const WebCore::Region& region) { WebCore::CCOcclusionTrackerBase<LayerType, RenderSurfaceType>::m_stack.last().occlusionInTarget = region; } +}; + +typedef TestCCOcclusionTrackerBase<WebCore::LayerChromium, WebCore::RenderSurfaceChromium> TestCCOcclusionTracker; +typedef TestCCOcclusionTrackerBase<WebCore::CCLayerImpl, WebCore::CCRenderSurface> TestCCOcclusionTrackerImpl; + +} + +#endif // CCOcclusionTrackerTestCommon_h diff --git a/Source/WebKit/chromium/tests/CCQuadCullerTest.cpp b/Source/WebKit/chromium/tests/CCQuadCullerTest.cpp index 42776bc94..991cddc46 100644 --- a/Source/WebKit/chromium/tests/CCQuadCullerTest.cpp +++ b/Source/WebKit/chromium/tests/CCQuadCullerTest.cpp @@ -26,6 +26,10 @@ #include "cc/CCQuadCuller.h" +#include "cc/CCOcclusionTracker.h" +#include "cc/CCOverdrawMetrics.h" +#include "cc/CCSingleThreadProxy.h" +#include "cc/CCTiledLayerImpl.h" #include "cc/CCTileDrawQuad.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -34,89 +38,145 @@ using namespace WebCore; namespace { -class CCQuadCullerTest : public testing::Test { +class TestCCOcclusionTrackerImpl : public CCOcclusionTrackerImpl { +public: + TestCCOcclusionTrackerImpl(const IntRect& scissorRectInScreen, bool recordMetricsForFrame = true) + : CCOcclusionTrackerImpl(scissorRectInScreen, recordMetricsForFrame) + , m_scissorRectInScreen(scissorRectInScreen) + { + } + +protected: + virtual IntRect layerScissorRectInTargetSurface(const CCLayerImpl* layer) const { return m_scissorRectInScreen; } + +private: + IntRect m_scissorRectInScreen; }; -static PassOwnPtr<CCDrawQuad> MakeTileQuad(CCSharedQuadState* state, const IntRect& rect, const IntRect& opaqueRect = IntRect()) +typedef CCLayerIterator<CCLayerImpl, Vector<CCLayerImpl*>, CCRenderSurface, CCLayerIteratorActions::FrontToBack> CCLayerIteratorType; + +static PassOwnPtr<CCTiledLayerImpl> makeLayer(CCTiledLayerImpl* parent, const TransformationMatrix& drawTransform, const IntRect& layerRect, float opacity, bool opaque, const IntRect& layerOpaqueRect, Vector<CCLayerImpl*>& surfaceLayerList) { - return CCTileDrawQuad::create(state, rect, intersection(rect, opaqueRect), 1, IntPoint(1, 1), IntSize(100, 100), 0, false, false, false, false, false); + OwnPtr<CCTiledLayerImpl> layer = CCTiledLayerImpl::create(0); + OwnPtr<CCLayerTilingData> tiler = CCLayerTilingData::create(IntSize(100, 100), CCLayerTilingData::NoBorderTexels); + tiler->setBounds(layerRect.size()); + layer->setTilingData(*tiler); + layer->setSkipsDraw(false); + layer->setDrawTransform(drawTransform); + layer->setScreenSpaceTransform(drawTransform); + layer->setVisibleLayerRect(layerRect); + layer->setDrawOpacity(opacity); + layer->setOpaque(opaque); + + int textureId = 1; + for (int i = 0; i < tiler->numTilesX(); ++i) + for (int j = 0; j < tiler->numTilesY(); ++j) { + IntRect tileOpaqueRect = opaque ? tiler->tileBounds(i, j) : intersection(tiler->tileBounds(i, j), layerOpaqueRect); + layer->pushTileProperties(i, j, static_cast<Platform3DObject>(textureId++), tileOpaqueRect); + } + + if (!parent) { + layer->createRenderSurface(); + layer->setTargetRenderSurface(layer->renderSurface()); + surfaceLayerList.append(layer.get()); + layer->renderSurface()->layerList().append(layer.get()); + } else { + layer->setTargetRenderSurface(parent->targetRenderSurface()); + parent->renderSurface()->layerList().append(layer.get()); + } + + return layer.release(); } -void setQuads(CCSharedQuadState* rootState, CCSharedQuadState* childState, CCQuadList& quadList, const IntRect& opaqueRect = IntRect()) +static void appendQuads(CCQuadList& quadList, Vector<OwnPtr<CCSharedQuadState> >& sharedStateList, CCTiledLayerImpl* layer, CCLayerIteratorType& it, CCOcclusionTrackerImpl& occlusionTracker) { - quadList.clear(); - - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(100, 0), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(200, 0), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(0, 100), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(100, 100), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(200, 100), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(0, 200), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(100, 200), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(rootState, IntRect(IntPoint(200, 200), IntSize(100, 100)), opaqueRect)); - - quadList.append(MakeTileQuad(childState, IntRect(IntPoint(), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(childState, IntRect(IntPoint(100, 0), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(childState, IntRect(IntPoint(0, 100), IntSize(100, 100)), opaqueRect)); - quadList.append(MakeTileQuad(childState, IntRect(IntPoint(100, 100), IntSize(100, 100)), opaqueRect)); + occlusionTracker.enterLayer(it); + CCQuadCuller quadCuller(quadList, layer, &occlusionTracker); + OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + sharedStateList.append(sharedQuadState.release()); + occlusionTracker.leaveLayer(it); + ++it; } #define DECLARE_AND_INITIALIZE_TEST_QUADS \ + DebugScopedSetImplThread impl; \ CCQuadList quadList; \ - CCOverdrawCounts overdraw; \ + Vector<OwnPtr<CCSharedQuadState> > sharedStateList; \ + Vector<CCLayerImpl*> renderSurfaceLayerList; \ TransformationMatrix childTransform; \ IntSize rootSize = IntSize(300, 300); \ IntRect rootRect = IntRect(IntPoint(), rootSize); \ IntSize childSize = IntSize(200, 200); \ IntRect childRect = IntRect(IntPoint(), childSize); -TEST(CCQuadCullerTest, verifyCullChildLinesUpTopLeft) +TEST(CCQuadCullerTest, verifyNoCulling) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, false, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 40000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); +} + +TEST(CCQuadCullerTest, verifyCullChildLinesUpTopLeft) +{ + DECLARE_AND_INITIALIZE_TEST_QUADS + + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); + + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 9u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 90000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 40000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 40000, 1); } TEST(CCQuadCullerTest, verifyCullWhenChildOpacityNotOne) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 0.9, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 0.9f, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); - EXPECT_EQ(quadList.size(), 13u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 90000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 40000, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 40000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); } TEST(CCQuadCullerTest, verifyCullWhenChildOpaqueFlagFalse) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, false); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, false, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 13u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 90000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 40000, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 40000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); } TEST(CCQuadCullerTest, verifyCullCenterTileOnly) @@ -125,32 +185,33 @@ TEST(CCQuadCullerTest, verifyCullCenterTileOnly) childTransform.translate(50, 50); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 12u); - IntRect quadVisibleRect1 = quadList[1].get()->quadVisibleRect(); + IntRect quadVisibleRect1 = quadList[5].get()->quadVisibleRect(); EXPECT_EQ(quadVisibleRect1.height(), 50); - IntRect quadVisibleRect3 = quadList[3].get()->quadVisibleRect(); + IntRect quadVisibleRect3 = quadList[7].get()->quadVisibleRect(); EXPECT_EQ(quadVisibleRect3.width(), 50); - // Next index is 4, not 5, since centre quad culled. - IntRect quadVisibleRect4 = quadList[4].get()->quadVisibleRect(); + // Next index is 8, not 9, since centre quad culled. + IntRect quadVisibleRect4 = quadList[8].get()->quadVisibleRect(); EXPECT_EQ(quadVisibleRect4.width(), 50); EXPECT_EQ(quadVisibleRect4.x(), 250); - IntRect quadVisibleRect6 = quadList[6].get()->quadVisibleRect(); + IntRect quadVisibleRect6 = quadList[10].get()->quadVisibleRect(); EXPECT_EQ(quadVisibleRect6.height(), 50); EXPECT_EQ(quadVisibleRect6.y(), 250); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 100000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 30000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 100000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 30000, 1); } TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize1) @@ -159,32 +220,33 @@ TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize1) childTransform.translate(100, 100); - // Create root layer tile with extent (99.1, 99.1) -> (200.9, 200.9) to make + // Make the root layer's quad have extent (99.1, 99.1) -> (200.9, 200.9) to make // sure it doesn't get culled due to transform rounding. TransformationMatrix rootTransform; rootTransform.translate(99.1, 99.1); rootTransform.scale(1.018); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(rootTransform, TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + rootRect = childRect = IntRect(0, 0, 100, 100); - quadList.append(MakeTileQuad(rootState.get(), IntRect(IntPoint(), IntSize(100, 100)))); - quadList.append(MakeTileQuad(childState.get(), IntRect(IntPoint(), IntSize(100, 100)))); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, rootTransform, rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - EXPECT_EQ(quadList.size(), 2u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 2u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 20363, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 20363, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); } TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize2) { DECLARE_AND_INITIALIZE_TEST_QUADS - // Make the child quad slightly smaller than, and centred over, the root layer tile. + // Make the child's quad slightly smaller than, and centred over, the root layer tile. // Verify the child does not cause the quad below to be culled due to rounding. childTransform.translate(100.1, 100.1); childTransform.scale(0.982); @@ -192,19 +254,20 @@ TEST(CCQuadCullerTest, verifyCullCenterTileNonIntegralSize2) TransformationMatrix rootTransform; rootTransform.translate(100, 100); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(rootTransform, TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + rootRect = childRect = IntRect(0, 0, 100, 100); - quadList.append(MakeTileQuad(rootState.get(), IntRect(IntPoint(), IntSize(100, 100)))); - quadList.append(MakeTileQuad(childState.get(), IntRect(IntPoint(), IntSize(100, 100)))); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, rootTransform, rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - EXPECT_EQ(quadList.size(), 2u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 2u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 19643, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 19643, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); } TEST(CCQuadCullerTest, verifyCullChildLinesUpBottomRight) @@ -213,16 +276,17 @@ TEST(CCQuadCullerTest, verifyCullChildLinesUpBottomRight) childTransform.translate(100, 100); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 9u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 90000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 40000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 40000, 1); } TEST(CCQuadCullerTest, verifyCullSubRegion) @@ -231,17 +295,18 @@ TEST(CCQuadCullerTest, verifyCullSubRegion) childTransform.translate(50, 50); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, false); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); IntRect childOpaqueRect(childRect.x() + childRect.width() / 4, childRect.y() + childRect.height() / 4, childRect.width() / 2, childRect.height() / 2); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, false, childOpaqueRect, renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList, childOpaqueRect); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 12u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 90000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 30000, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 10000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 30000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 10000, 1); } TEST(CCQuadCullerTest, verifyCullSubRegion2) @@ -250,17 +315,18 @@ TEST(CCQuadCullerTest, verifyCullSubRegion2) childTransform.translate(50, 10); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, false); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); IntRect childOpaqueRect(childRect.x() + childRect.width() / 4, childRect.y() + childRect.height() / 4, childRect.width() / 2, childRect.height() * 3 / 4); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, false, childOpaqueRect, renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList, childOpaqueRect); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 12u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 90000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 25000, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 15000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 25000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 15000, 1); } TEST(CCQuadCullerTest, verifyCullSubRegionCheckOvercull) @@ -269,14 +335,18 @@ TEST(CCQuadCullerTest, verifyCullSubRegionCheckOvercull) childTransform.translate(50, 49); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, false); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); IntRect childOpaqueRect(childRect.x() + childRect.width() / 4, childRect.y() + childRect.height() / 4, childRect.width() / 2, childRect.height() / 2); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, false, childOpaqueRect, renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList, childOpaqueRect); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 13u); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 90000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 30000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 10000, 1); } TEST(CCQuadCullerTest, verifyNonAxisAlignedQuadsDontOcclude) @@ -286,16 +356,17 @@ TEST(CCQuadCullerTest, verifyNonAxisAlignedQuadsDontOcclude) // Use a small rotation so as to not disturb the geometry significantly. childTransform.rotate(1); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), childTransform, childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 13u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 130000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 130000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); } // This test requires some explanation: here we are rotating the quads to be culled. @@ -311,74 +382,102 @@ TEST(CCQuadCullerTest, verifyNonAxisAlignedQuadsSafelyCulled) TransformationMatrix parentTransform; parentTransform.rotate(1); - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(parentTransform, TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, parentTransform, rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(-100, -100, 1000, 1000)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, false, IntRect(), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 12u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 120000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 10000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 100600, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 29400, 1); } TEST(CCQuadCullerTest, verifyCullOutsideScissorOverTile) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(200, 100, 100, 100)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, true, IntRect(200, 100, 100, 100), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 1u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 10000, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 120000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 10000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 120000, 1); } TEST(CCQuadCullerTest, verifyCullOutsideScissorOverCulledTile) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(100, 100, 100, 100)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, true, IntRect(100, 100, 100, 100), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 1u); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 10000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 120000, 1); } TEST(CCQuadCullerTest, verifyCullOutsideScissorOverPartialTiles) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(50, 50, 200, 200)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, true, IntRect(50, 50, 200, 200), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 9u); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 40000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 90000, 1); } TEST(CCQuadCullerTest, verifyCullOutsideScissorOverNoTiles) { DECLARE_AND_INITIALIZE_TEST_QUADS - OwnPtr<CCSharedQuadState> rootState = CCSharedQuadState::create(TransformationMatrix(), TransformationMatrix(), rootRect, IntRect(), 1.0, true); - OwnPtr<CCSharedQuadState> childState = CCSharedQuadState::create(childTransform, TransformationMatrix(), childRect, IntRect(), 1.0, true); + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(500, 500, 100, 100)); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); - setQuads(rootState.get(), childState.get(), quadList); - EXPECT_EQ(quadList.size(), 13u); - CCQuadCuller::cullOccludedQuads(quadList, true, IntRect(500, 500, 100, 100), &overdraw); + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); EXPECT_EQ(quadList.size(), 0u); - EXPECT_NEAR(overdraw.m_pixelsDrawnOpaque, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsDrawnTransparent, 0, 1); - EXPECT_NEAR(overdraw.m_pixelsCulled, 130000, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 130000, 1); +} + +TEST(CCQuadCullerTest, verifyWithoutMetrics) +{ + DECLARE_AND_INITIALIZE_TEST_QUADS + + OwnPtr<CCTiledLayerImpl> rootLayer = makeLayer(0, TransformationMatrix(), rootRect, 1.0, true, IntRect(), renderSurfaceLayerList); + OwnPtr<CCTiledLayerImpl> childLayer = makeLayer(rootLayer.get(), TransformationMatrix(), childRect, 1.0, true, IntRect(), renderSurfaceLayerList); + TestCCOcclusionTrackerImpl occlusionTracker(IntRect(50, 50, 200, 200), false); + CCLayerIteratorType it = CCLayerIteratorType::begin(&renderSurfaceLayerList); + + appendQuads(quadList, sharedStateList, childLayer.get(), it, occlusionTracker); + appendQuads(quadList, sharedStateList, rootLayer.get(), it, occlusionTracker); + EXPECT_EQ(quadList.size(), 9u); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnOpaque(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsDrawnTranslucent(), 0, 1); + EXPECT_NEAR(occlusionTracker.overdrawMetrics().pixelsCulledForDrawing(), 0, 1); } diff --git a/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp b/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp index 1201587c7..7dac005d2 100644 --- a/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp +++ b/Source/WebKit/chromium/tests/CCRenderSurfaceTest.cpp @@ -26,7 +26,9 @@ #include "cc/CCRenderSurface.h" +#include "TransformationMatrix.h" #include "cc/CCLayerImpl.h" +#include "cc/CCSharedQuadState.h" #include "cc/CCSingleThreadProxy.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -89,4 +91,38 @@ TEST(CCRenderSurfaceTest, verifySurfaceChangesAreTrackedProperly) EXECUTE_AND_VERIFY_SURFACE_DID_NOT_CHANGE(renderSurface->setMaskLayer(dummyMask.get())); } +TEST(CCRenderSurfaceTest, sanityCheckSurfaceCreatesCorrectSharedQuadState) +{ + // This will fake that we are on the correct thread for testing purposes. + DebugScopedSetImplThread setImplThread; + + OwnPtr<CCLayerImpl> owningLayer = CCLayerImpl::create(0); + owningLayer->createRenderSurface(); + ASSERT_TRUE(owningLayer->renderSurface()); + CCRenderSurface* renderSurface = owningLayer->renderSurface(); + + IntRect contentRect = IntRect(IntPoint::zero(), IntSize(50, 50)); + IntRect clipRect = IntRect(IntPoint(5, 5), IntSize(40, 40)); + TransformationMatrix draw; + TransformationMatrix origin; + + draw.translate(30, 40); + + renderSurface->setDrawTransform(draw); + renderSurface->setOriginTransform(origin); + renderSurface->setContentRect(contentRect); + renderSurface->setClipRect(clipRect); + renderSurface->setDrawOpacity(1); + + OwnPtr<CCSharedQuadState> sharedQuadState = renderSurface->createSharedQuadState(); + + EXPECT_TRUE(sharedQuadState->quadTransform().isIdentity()); + EXPECT_EQ(30, sharedQuadState->layerTransform().m41()); + EXPECT_EQ(40, sharedQuadState->layerTransform().m42()); + EXPECT_EQ(contentRect, sharedQuadState->layerRect()); + EXPECT_EQ(clipRect, sharedQuadState->clipRect()); + EXPECT_EQ(1, sharedQuadState->opacity()); + EXPECT_FALSE(sharedQuadState->isOpaque()); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp b/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp index 0853e5a36..2731c76fd 100644 --- a/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp +++ b/Source/WebKit/chromium/tests/CCSchedulerStateMachineTest.cpp @@ -52,6 +52,9 @@ public: void setNeedsCommit(bool b) { m_needsCommit = b; } bool needsCommit() const { return m_needsCommit; } + void setNeedsForcedCommit(bool b) { m_needsForcedCommit = b; } + bool needsForcedCommit() const { return m_needsForcedCommit; } + void setNeedsRedraw(bool b) { m_needsRedraw = b; } bool needsRedraw() const { return m_needsRedraw; } @@ -72,6 +75,7 @@ TEST(CCSchedulerStateMachineTest, TestNextActionBeginsFrameIfNeeded) { StateMachine state; state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_IDLE); + state.setCanBeginFrame(true); state.setNeedsRedraw(false); state.setNeedsCommit(false); state.setUpdateMoreResourcesPending(false); @@ -86,10 +90,30 @@ TEST(CCSchedulerStateMachineTest, TestNextActionBeginsFrameIfNeeded) EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); } + // If commit requested but canBeginFrame is still false, do nothing. + { + StateMachine state; + state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_IDLE); + state.setNeedsRedraw(false); + state.setNeedsCommit(false); + state.setUpdateMoreResourcesPending(false); + state.setVisible(true); + + EXPECT_FALSE(state.vsyncCallbackNeeded()); + + state.didLeaveVSync(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + EXPECT_FALSE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + } + + // If commit requested, begin a frame { StateMachine state; state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_IDLE); + state.setCanBeginFrame(true); state.setNeedsRedraw(false); state.setNeedsCommit(true); state.setUpdateMoreResourcesPending(false); @@ -100,6 +124,7 @@ TEST(CCSchedulerStateMachineTest, TestNextActionBeginsFrameIfNeeded) // Begin the frame, make sure needsCommit and commitState update correctly. { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME); EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS, state.commitState()); @@ -116,6 +141,180 @@ TEST(CCSchedulerStateMachineTest, TestSetForcedRedrawDoesNotSetsNormalRedraw) EXPECT_TRUE(state.vsyncCallbackNeeded()); } +TEST(CCSchedulerStateMachineTest, TestFailedDrawSetsNeedsCommitAndDoesNotDrawAgain) +{ + CCSchedulerStateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + state.setNeedsRedraw(); + EXPECT_TRUE(state.redrawPending()); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + + // We're drawing now. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + EXPECT_FALSE(state.redrawPending()); + EXPECT_FALSE(state.commitPending()); + + // Failing the draw makes us require a commit. + state.didDrawIfPossibleCompleted(false); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME); + EXPECT_TRUE(state.redrawPending()); + EXPECT_TRUE(state.commitPending()); +} + +TEST(CCSchedulerStateMachineTest, TestSetNeedsRedrawDuringFailedDrawDoesNotRemoveNeedsRedraw) +{ + CCSchedulerStateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + state.setNeedsRedraw(); + EXPECT_TRUE(state.redrawPending()); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + + // We're drawing now. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + EXPECT_FALSE(state.redrawPending()); + EXPECT_FALSE(state.commitPending()); + + // While still in the same vsync callback, set needs redraw again. + // This should not redraw. + state.setNeedsRedraw(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + + // Failing the draw makes us require a commit. + state.didDrawIfPossibleCompleted(false); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); + EXPECT_TRUE(state.redrawPending()); +} + +TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedDrawAllowsDrawInSameFrame) +{ + CCSchedulerStateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + + // Start a commit. + state.setNeedsCommit(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME); + EXPECT_TRUE(state.commitPending()); + + // Then initiate a draw. + state.setNeedsRedraw(); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + EXPECT_TRUE(state.redrawPending()); + + // Fail the draw. + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + state.didDrawIfPossibleCompleted(false); + EXPECT_TRUE(state.redrawPending()); + // But the commit is ongoing. + EXPECT_TRUE(state.commitPending()); + + // Finish the commit. + state.beginFrameComplete(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES); + state.beginUpdateMoreResourcesComplete(false); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_COMMIT); + EXPECT_TRUE(state.redrawPending()); + + // And we should be allowed to draw again. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); +} + +TEST(CCSchedulerStateMachineTest, TestCommitAfterFailedAndSuccessfulDrawDoesNotAllowDrawInSameFrame) +{ + CCSchedulerStateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + + // Start a commit. + state.setNeedsCommit(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_FRAME); + EXPECT_TRUE(state.commitPending()); + + // Then initiate a draw. + state.setNeedsRedraw(); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + EXPECT_TRUE(state.redrawPending()); + + // Fail the draw. + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + state.didDrawIfPossibleCompleted(false); + EXPECT_TRUE(state.redrawPending()); + // But the commit is ongoing. + EXPECT_TRUE(state.commitPending()); + + // Force a draw. + state.setNeedsForcedRedraw(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction()); + + // Do the forced draw. + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_FORCED); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + EXPECT_FALSE(state.redrawPending()); + // And the commit is still ongoing. + EXPECT_TRUE(state.commitPending()); + + // Finish the commit. + state.beginFrameComplete(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES); + state.beginUpdateMoreResourcesComplete(false); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_COMMIT); + EXPECT_TRUE(state.redrawPending()); + + // And we should not be allowed to draw again in the same frame.. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); +} + +TEST(CCSchedulerStateMachineTest, TestFailedDrawIsRetriedNextVSync) +{ + CCSchedulerStateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + + // Start a draw. + state.setNeedsRedraw(); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + EXPECT_TRUE(state.redrawPending()); + + // Fail the draw. + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + state.didDrawIfPossibleCompleted(false); + EXPECT_TRUE(state.redrawPending()); + + // We should not be trying to draw again now, but we have a commit pending. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); + + state.didLeaveVSync(); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + state.didEnterVSync(); + + // We should try draw again in the next vsync. + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); +} + TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) { CCSchedulerStateMachine state; @@ -123,8 +322,8 @@ TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) state.setNeedsRedraw(); EXPECT_TRUE(state.vsyncCallbackNeeded()); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - state.updateState(CCSchedulerStateMachine::ACTION_DRAW); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); // While still in the same vsync callback, set needs redraw again. // This should not redraw. @@ -132,12 +331,14 @@ TEST(CCSchedulerStateMachineTest, TestDoestDrawTwiceInSameFrame) EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); // Move to another frame. This should now draw. + state.didDrawIfPossibleCompleted(true); state.didLeaveVSync(); EXPECT_TRUE(state.vsyncCallbackNeeded()); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - state.updateState(CCSchedulerStateMachine::ACTION_DRAW); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + state.didDrawIfPossibleCompleted(true); EXPECT_FALSE(state.vsyncCallbackNeeded()); } @@ -156,31 +357,13 @@ TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync) } else state.setVisible(true); - // Case 1: needsCommit=false updateMoreResourcesPending=false. - state.setNeedsCommit(false); - state.setUpdateMoreResourcesPending(false); - EXPECT_FALSE(state.vsyncCallbackNeeded()); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - - // Case 2: needsCommit=false updateMoreResourcesPending=true. + // Case 1: needsCommit=false state.setNeedsCommit(false); - state.setUpdateMoreResourcesPending(true); - if (visible) - EXPECT_TRUE(state.vsyncCallbackNeeded()); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); - // Case 3: needsCommit=true updateMoreResourcesPending=false. + // Case 2: needsCommit=true state.setNeedsCommit(true); - state.setUpdateMoreResourcesPending(false); - EXPECT_FALSE(state.vsyncCallbackNeeded()); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - - // Case 4: needsCommit=true updateMoreResourcesPending=true. - state.setNeedsCommit(true); - state.setUpdateMoreResourcesPending(true); - if (visible) - EXPECT_TRUE(state.vsyncCallbackNeeded()); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); } } @@ -189,7 +372,8 @@ TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync) for (unsigned j = 0; j < 2; ++j) { StateMachine state; state.setCommitState(allCommitStates[i]); - if (!j) { + bool forcedDraw = j; + if (!forcedDraw) { state.didEnterVSync(); state.setNeedsRedraw(true); state.setVisible(true); @@ -198,7 +382,7 @@ TEST(CCSchedulerStateMachineTest, TestNextActionDrawsOnVSync) CCSchedulerStateMachine::Action expectedAction; if (allCommitStates[i] != CCSchedulerStateMachine::COMMIT_STATE_READY_TO_COMMIT) - expectedAction = CCSchedulerStateMachine::ACTION_DRAW; + expectedAction = forcedDraw ? CCSchedulerStateMachine::ACTION_DRAW_FORCED : CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE; else expectedAction = CCSchedulerStateMachine::ACTION_COMMIT; @@ -246,22 +430,22 @@ TEST(CCSchedulerStateMachineTest, TestNoCommitStatesRedrawWhenInvisible) // Case 1: needsCommit=false updateMoreResourcesPending=false. state.setNeedsCommit(false); state.setUpdateMoreResourcesPending(false); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); // Case 2: needsCommit=false updateMoreResourcesPending=true. state.setNeedsCommit(false); state.setUpdateMoreResourcesPending(true); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); // Case 3: needsCommit=true updateMoreResourcesPending=false. state.setNeedsCommit(true); state.setUpdateMoreResourcesPending(false); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); // Case 4: needsCommit=true updateMoreResourcesPending=true. state.setNeedsCommit(true); state.setUpdateMoreResourcesPending(true); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); } } } @@ -281,7 +465,7 @@ TEST(CCSchedulerStateMachineTest, TestCanRedraw_StopsDraw) state.didEnterVSync(); state.setCanDraw(false); - EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_NE(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); } } } @@ -290,6 +474,7 @@ TEST(CCSchedulerStateMachineTest, TestCanRedrawWithWaitingForFirstDrawMakesProgr { StateMachine state; state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW); + state.setCanBeginFrame(true); state.setNeedsCommit(true); state.setNeedsRedraw(true); state.setUpdateMoreResourcesPending(false); @@ -368,6 +553,20 @@ TEST(CCSchedulerStateMachineTest, TestUpdates_NoRedraw_TwoRoundsOfUpdates) EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction()); } + +TEST(CCSchedulerStateMachineTest, TestVSyncNeededWhenUpdatesPendingButInvisible) +{ + StateMachine state; + state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_UPDATING_RESOURCES); + state.setNeedsRedraw(false); + state.setVisible(false); + state.setUpdateMoreResourcesPending(true); + EXPECT_TRUE(state.vsyncCallbackNeeded()); + + state.setUpdateMoreResourcesPending(false); + EXPECT_TRUE(state.vsyncCallbackNeeded()); +} + TEST(CCSchedulerStateMachineTest, TestUpdates_WithRedraw_OneRoundOfUpdates) { StateMachine state; @@ -382,8 +581,9 @@ TEST(CCSchedulerStateMachineTest, TestUpdates_WithRedraw_OneRoundOfUpdates) // Ensure we draw on the next vsync even though an update is in-progress. state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - state.updateState(CCSchedulerStateMachine::ACTION_DRAW); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + state.didDrawIfPossibleCompleted(true); // Ensure that we once we have drawn, we dont do anything else. EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); @@ -418,6 +618,7 @@ TEST(CCSchedulerStateMachineTest, TestUpdates_WithRedraw_OneRoundOfUpdates) TEST(CCSchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) { StateMachine state; + state.setCanBeginFrame(true); state.setNeedsCommit(true); state.setVisible(true); @@ -447,9 +648,10 @@ TEST(CCSchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) // Commit and make sure we draw on next vsync state.updateState(CCSchedulerStateMachine::ACTION_COMMIT); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState()); - state.updateState(CCSchedulerStateMachine::ACTION_DRAW); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + state.didDrawIfPossibleCompleted(true); // Verify that another commit will begin. state.didLeaveVSync(); @@ -459,6 +661,7 @@ TEST(CCSchedulerStateMachineTest, TestSetNeedsCommitIsNotLost) TEST(CCSchedulerStateMachineTest, TestFullCycle) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); // Start clean and set commit. @@ -492,8 +695,9 @@ TEST(CCSchedulerStateMachineTest, TestFullCycle) // At vsync, draw. state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - state.updateState(CCSchedulerStateMachine::ACTION_DRAW); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + state.didDrawIfPossibleCompleted(true); state.didLeaveVSync(); // Should be synchronized, no draw needed, no action needed. @@ -505,6 +709,7 @@ TEST(CCSchedulerStateMachineTest, TestFullCycle) TEST(CCSchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); // Start clean and set commit. @@ -542,8 +747,9 @@ TEST(CCSchedulerStateMachineTest, TestFullCycleWithCommitRequestInbetween) // At vsync, draw. state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); - state.updateState(CCSchedulerStateMachine::ACTION_DRAW); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); + state.updateState(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE); + state.didDrawIfPossibleCompleted(true); state.didLeaveVSync(); // Should be synchronized, no draw needed, no action needed. @@ -562,6 +768,7 @@ TEST(CCSchedulerStateMachineTest, TestRequestCommitInvisible) TEST(CCSchedulerStateMachineTest, TestGoesInvisibleMidCommit) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); // Start clean and set commit. @@ -607,6 +814,7 @@ TEST(CCSchedulerStateMachineTest, TestGoesInvisibleMidCommit) TEST(CCSchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); state.didLoseContext(); @@ -628,6 +836,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhenCompletelyIdle) TEST(CCSchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhileRecreating) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); state.didLoseContext(); @@ -653,7 +862,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhile // setCanDraw. state.setNeedsRedraw(true); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); state.setCanDraw(false); EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); state.setCanDraw(true); @@ -663,6 +872,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhenIdleAndCommitRequestedWhile TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgress) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); // Get a commit in flight. @@ -673,7 +883,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgress) // Set damage and expect a draw. state.setNeedsRedraw(true); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); state.updateState(state.nextAction()); state.didLeaveVSync(); @@ -703,6 +913,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgress) TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnotherCommitRequested) { StateMachine state; + state.setCanBeginFrame(true); state.setVisible(true); // Get a commit in flight. @@ -713,7 +924,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnother // Set damage and expect a draw. state.setNeedsRedraw(true); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); state.updateState(state.nextAction()); state.didLeaveVSync(); @@ -739,7 +950,7 @@ TEST(CCSchedulerStateMachineTest, TestContextLostWhileCommitInProgressAndAnother EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_WAITING_FOR_FIRST_DRAW, state.commitState()); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_IF_POSSIBLE, state.nextAction()); state.updateState(state.nextAction()); // Expect to be told to begin context recreation, independent of vsync state @@ -761,7 +972,7 @@ TEST(CCSchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) // Ask a forced redraw and verify it ocurrs. state.setNeedsForcedRedraw(true); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction()); state.didLeaveVSync(); // Clear the forced redraw bit. @@ -774,8 +985,60 @@ TEST(CCSchedulerStateMachineTest, TestFinishAllRenderingWhileContextLost) // Ask a forced redraw and verify it ocurrs. state.setNeedsForcedRedraw(true); state.didEnterVSync(); - EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW, state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_DRAW_FORCED, state.nextAction()); state.didLeaveVSync(); } +TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenInvisibleAndForceCommit) +{ + StateMachine state; + state.setCanBeginFrame(true); + state.setVisible(false); + state.setNeedsCommit(true); + state.setNeedsForcedCommit(true); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); +} + +TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenCanBeginFrameFalseAndForceCommit) +{ + StateMachine state; + state.setVisible(true); + state.setNeedsCommit(true); + state.setNeedsForcedCommit(true); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); +} + +TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenCommitInProgress) +{ + StateMachine state; + state.setCanBeginFrame(true); + state.setVisible(false); + state.setCommitState(CCSchedulerStateMachine::COMMIT_STATE_FRAME_IN_PROGRESS); + state.setNeedsCommit(true); + state.setNeedsForcedCommit(true); + + state.beginFrameComplete(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_UPDATE_MORE_RESOURCES, state.nextAction()); + state.updateState(state.nextAction()); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_NONE, state.nextAction()); + state.beginUpdateMoreResourcesComplete(false); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_COMMIT, state.nextAction()); + state.updateState(state.nextAction()); + + EXPECT_EQ(CCSchedulerStateMachine::COMMIT_STATE_IDLE, state.commitState()); + + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); +} + +TEST(CCSchedulerStateMachineTest, TestBeginFrameWhenContextLost) +{ + StateMachine state; + state.setCanBeginFrame(true); + state.setVisible(true); + state.setNeedsCommit(true); + state.setNeedsForcedCommit(true); + state.didLoseContext(); + EXPECT_EQ(CCSchedulerStateMachine::ACTION_BEGIN_FRAME, state.nextAction()); +} + } diff --git a/Source/WebKit/chromium/tests/CCSchedulerTest.cpp b/Source/WebKit/chromium/tests/CCSchedulerTest.cpp index 45f61b90b..41250884b 100644 --- a/Source/WebKit/chromium/tests/CCSchedulerTest.cpp +++ b/Source/WebKit/chromium/tests/CCSchedulerTest.cpp @@ -45,25 +45,56 @@ public: m_actions.clear(); m_hasMoreResourceUpdates = false; m_canDraw = true; + m_drawWillHappen = true; + m_swapWillHappenIfDrawHappens = true; + m_numDraws = 0; } void setHasMoreResourceUpdates(bool b) { m_hasMoreResourceUpdates = b; } void setCanDraw(bool b) { m_canDraw = b; } + int numDraws() const { return m_numDraws; } int numActions() const { return static_cast<int>(m_actions.size()); } const char* action(int i) const { return m_actions[i]; } - virtual bool canDraw() { return m_canDraw; } - virtual bool hasMoreResourceUpdates() const { return m_hasMoreResourceUpdates; } - virtual void scheduledActionBeginFrame() { m_actions.push_back("scheduledActionBeginFrame"); } - virtual void scheduledActionDrawAndSwap() { m_actions.push_back("scheduledActionDrawAndSwap"); } - virtual void scheduledActionUpdateMoreResources() { m_actions.push_back("scheduledActionUpdateMoreResources"); } - virtual void scheduledActionCommit() { m_actions.push_back("scheduledActionCommit"); } - virtual void scheduledActionBeginContextRecreation() { m_actions.push_back("scheduledActionBeginContextRecreation"); } + bool hasAction(const char* action) const + { + for (size_t i = 0; i < m_actions.size(); i++) + if (!strcmp(m_actions[i], action)) + return true; + return false; + } + + virtual bool canDraw() OVERRIDE { return m_canDraw; } + virtual bool hasMoreResourceUpdates() const OVERRIDE { return m_hasMoreResourceUpdates; } + virtual void scheduledActionBeginFrame() OVERRIDE { m_actions.push_back("scheduledActionBeginFrame"); } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE + { + m_actions.push_back("scheduledActionDrawAndSwapIfPossible"); + m_numDraws++; + return CCScheduledActionDrawAndSwapResult(m_drawWillHappen, m_drawWillHappen && m_swapWillHappenIfDrawHappens); + } + + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE + { + m_actions.push_back("scheduledActionDrawAndSwapForced"); + return CCScheduledActionDrawAndSwapResult(true, m_swapWillHappenIfDrawHappens); + } + + virtual void scheduledActionUpdateMoreResources() OVERRIDE { m_actions.push_back("scheduledActionUpdateMoreResources"); } + virtual void scheduledActionCommit() OVERRIDE { m_actions.push_back("scheduledActionCommit"); } + virtual void scheduledActionBeginContextRecreation() OVERRIDE { m_actions.push_back("scheduledActionBeginContextRecreation"); } + virtual void scheduledActionAcquireLayerTexturesForMainThread() OVERRIDE { m_actions.push_back("scheduledActionAcquireLayerTexturesForMainThread"); } + + void setDrawWillHappen(bool drawWillHappen) { m_drawWillHappen = drawWillHappen; } + void setSwapWillHappenIfDrawHappens(bool swapWillHappenIfDrawHappens) { m_swapWillHappenIfDrawHappens = swapWillHappenIfDrawHappens; } protected: bool m_hasMoreResourceUpdates; bool m_canDraw; + bool m_drawWillHappen; + bool m_swapWillHappenIfDrawHappens; + int m_numDraws; std::vector<const char*> m_actions; }; @@ -72,6 +103,7 @@ TEST(CCSchedulerTest, RequestCommit) FakeCCSchedulerClient client; RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); scheduler->setVisible(true); // SetNeedsCommit should begin the frame. @@ -94,7 +126,7 @@ TEST(CCSchedulerTest, RequestCommit) // Tick should draw. timeSource->tick(); EXPECT_EQ(1, client.numActions()); - EXPECT_STREQ("scheduledActionDrawAndSwap", client.action(0)); + EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0)); EXPECT_FALSE(timeSource->active()); client.reset(); @@ -107,6 +139,7 @@ TEST(CCSchedulerTest, RequestCommitAfterBeginFrame) FakeCCSchedulerClient client; RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); scheduler->setVisible(true); // SetNedsCommit should begin the frame. @@ -131,38 +164,104 @@ TEST(CCSchedulerTest, RequestCommitAfterBeginFrame) timeSource->tick(); EXPECT_FALSE(timeSource->active()); EXPECT_EQ(2, client.numActions()); - EXPECT_STREQ("scheduledActionDrawAndSwap", client.action(0)); + EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0)); EXPECT_STREQ("scheduledActionBeginFrame", client.action(1)); client.reset(); } -class SchedulerClientThatSetNeedsDrawInsideDraw : public CCSchedulerClient { +TEST(CCSchedulerTest, TextureAcquisitionCollision) +{ + FakeCCSchedulerClient client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + scheduler->setNeedsCommit(); + scheduler->setMainThreadNeedsLayerTextures(); + EXPECT_EQ(2, client.numActions()); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(0)); + EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(1)); + client.reset(); + + // Compositor not scheduled to draw because textures are locked by main thread + EXPECT_FALSE(timeSource->active()); + + // Trigger the commit + scheduler->beginFrameComplete(); + EXPECT_TRUE(timeSource->active()); + client.reset(); + + // Between commit and draw, texture acquisition for main thread delayed, + // and main thread blocks. + scheduler->setMainThreadNeedsLayerTextures(); + EXPECT_EQ(0, client.numActions()); + client.reset(); + + // Once compositor draw complete, the delayed texture acquisition fires. + timeSource->tick(); + EXPECT_EQ(3, client.numActions()); + EXPECT_STREQ("scheduledActionDrawAndSwapIfPossible", client.action(0)); + EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(1)); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(2)); + client.reset(); +} + +TEST(CCSchedulerTest, VisibilitySwitchWithTextureAcquisition) +{ + FakeCCSchedulerClient client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + scheduler->setNeedsCommit(); + scheduler->beginFrameComplete(); + scheduler->setMainThreadNeedsLayerTextures(); + client.reset(); + // Verify that pending texture acquisition fires when visibility + // is lost in order to avoid a deadlock. + scheduler->setVisible(false); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionAcquireLayerTexturesForMainThread", client.action(0)); + client.reset(); + + // Regaining visibility with textures acquired by main thread while + // compositor is waiting for first draw should result in a request + // for a new frame in order to escape a deadlock. + scheduler->setVisible(true); + EXPECT_EQ(1, client.numActions()); + EXPECT_STREQ("scheduledActionBeginFrame", client.action(0)); + client.reset(); +} + +class SchedulerClientThatSetNeedsDrawInsideDraw : public FakeCCSchedulerClient { public: SchedulerClientThatSetNeedsDrawInsideDraw() - : m_numDraws(0) - , m_scheduler(0) { } + : m_scheduler(0) { } void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; } - int numDraws() const { return m_numDraws; } - - virtual bool hasMoreResourceUpdates() const { return false; } - virtual bool canDraw() { return true; } - virtual void scheduledActionBeginFrame() { } - virtual void scheduledActionDrawAndSwap() + virtual void scheduledActionBeginFrame() OVERRIDE { } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE { // Only setNeedsRedraw the first time this is called if (!m_numDraws) m_scheduler->setNeedsRedraw(); - m_numDraws++; + return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible(); } - virtual void scheduledActionUpdateMoreResources() { } - virtual void scheduledActionCommit() { } - virtual void scheduledActionBeginContextRecreation() { } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE + { + ASSERT_NOT_REACHED(); + return CCScheduledActionDrawAndSwapResult(true, true); + } + + virtual void scheduledActionUpdateMoreResources() OVERRIDE { } + virtual void scheduledActionCommit() OVERRIDE { } + virtual void scheduledActionBeginContextRecreation() OVERRIDE { } protected: - int m_numDraws; CCScheduler* m_scheduler; }; @@ -176,6 +275,7 @@ TEST(CCSchedulerTest, RequestRedrawInsideDraw) RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); scheduler->setVisible(true); scheduler->setNeedsRedraw(); @@ -194,33 +294,74 @@ TEST(CCSchedulerTest, RequestRedrawInsideDraw) EXPECT_FALSE(timeSource->active()); } -class SchedulerClientThatSetNeedsCommitInsideDraw : public CCSchedulerClient { +// Test that requesting redraw inside a failed draw doesn't lose the request. +TEST(CCSchedulerTest, RequestRedrawInsideFailedDraw) +{ + SchedulerClientThatSetNeedsDrawInsideDraw client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + client.setDrawWillHappen(false); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + // Fail the draw. + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + + // We have a commit pending and the draw failed, and we didn't lose the redraw request. + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Fail the draw again. + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Draw successfully. + client.setDrawWillHappen(true); + timeSource->tick(); + EXPECT_EQ(3, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_FALSE(scheduler->redrawPending()); + EXPECT_FALSE(timeSource->active()); +} + +class SchedulerClientThatSetNeedsCommitInsideDraw : public FakeCCSchedulerClient { public: SchedulerClientThatSetNeedsCommitInsideDraw() - : m_numDraws(0) - , m_scheduler(0) { } + : m_scheduler(0) { } void setScheduler(CCScheduler* scheduler) { m_scheduler = scheduler; } - int numDraws() const { return m_numDraws; } - - virtual bool hasMoreResourceUpdates() const { return false; } - virtual bool canDraw() { return true; } - virtual void scheduledActionBeginFrame() { } - virtual void scheduledActionDrawAndSwap() + virtual void scheduledActionBeginFrame() OVERRIDE { } + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapIfPossible() OVERRIDE { // Only setNeedsCommit the first time this is called if (!m_numDraws) m_scheduler->setNeedsCommit(); - m_numDraws++; + return FakeCCSchedulerClient::scheduledActionDrawAndSwapIfPossible(); + } + + virtual CCScheduledActionDrawAndSwapResult scheduledActionDrawAndSwapForced() OVERRIDE + { + ASSERT_NOT_REACHED(); + return CCScheduledActionDrawAndSwapResult(true, true); } - virtual void scheduledActionUpdateMoreResources() { } - virtual void scheduledActionCommit() { } - virtual void scheduledActionBeginContextRecreation() { } + virtual void scheduledActionUpdateMoreResources() OVERRIDE { } + virtual void scheduledActionCommit() OVERRIDE { } + virtual void scheduledActionBeginContextRecreation() OVERRIDE { } protected: - int m_numDraws; CCScheduler* m_scheduler; }; @@ -232,6 +373,7 @@ TEST(CCSchedulerTest, RequestCommitInsideDraw) RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); scheduler->setVisible(true); scheduler->setNeedsRedraw(); @@ -251,4 +393,104 @@ TEST(CCSchedulerTest, RequestCommitInsideDraw) EXPECT_FALSE(scheduler->redrawPending()); } +// Tests that when a draw fails then the pending commit should not be dropped. +TEST(CCSchedulerTest, RequestCommitInsideFailedDraw) +{ + SchedulerClientThatSetNeedsDrawInsideDraw client; + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, adoptPtr(new CCFrameRateController(timeSource))); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + client.setDrawWillHappen(false); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + // Fail the draw. + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + + // We have a commit pending and the draw failed, and we didn't lose the commit request. + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Fail the draw again. + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Draw successfully. + client.setDrawWillHappen(true); + timeSource->tick(); + EXPECT_EQ(3, client.numDraws()); + EXPECT_TRUE(scheduler->commitPending()); + EXPECT_FALSE(scheduler->redrawPending()); + EXPECT_FALSE(timeSource->active()); +} + +TEST(CCSchedulerTest, NoBeginFrameWhenDrawFails) +{ + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + SchedulerClientThatSetNeedsCommitInsideDraw client; + OwnPtr<FakeCCFrameRateController> controller = adoptPtr(new FakeCCFrameRateController(timeSource)); + FakeCCFrameRateController* controllerPtr = controller.get(); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, controller.release()); + client.setScheduler(scheduler.get()); + scheduler->setCanBeginFrame(true); + scheduler->setVisible(true); + + EXPECT_EQ(0, controllerPtr->numFramesPending()); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + EXPECT_EQ(0, client.numDraws()); + + // Draw successfully, this starts a new frame. + timeSource->tick(); + EXPECT_EQ(1, client.numDraws()); + EXPECT_EQ(1, controllerPtr->numFramesPending()); + scheduler->didSwapBuffersComplete(); + EXPECT_EQ(0, controllerPtr->numFramesPending()); + + scheduler->setNeedsRedraw(); + EXPECT_TRUE(scheduler->redrawPending()); + EXPECT_TRUE(timeSource->active()); + + // Fail to draw, this should not start a frame. + client.setDrawWillHappen(false); + timeSource->tick(); + EXPECT_EQ(2, client.numDraws()); + EXPECT_EQ(0, controllerPtr->numFramesPending()); +} + +TEST(CCSchedulerTest, NoBeginFrameWhenSwapFailsDuringForcedCommit) +{ + RefPtr<FakeCCTimeSource> timeSource = adoptRef(new FakeCCTimeSource()); + FakeCCSchedulerClient client; + OwnPtr<FakeCCFrameRateController> controller = adoptPtr(new FakeCCFrameRateController(timeSource)); + FakeCCFrameRateController* controllerPtr = controller.get(); + OwnPtr<CCScheduler> scheduler = CCScheduler::create(&client, controller.release()); + + EXPECT_EQ(0, controllerPtr->numFramesPending()); + + // Tell the client that it will fail to swap. + client.setDrawWillHappen(true); + client.setSwapWillHappenIfDrawHappens(false); + + // Get the compositor to do a scheduledActionDrawAndSwapForced. + scheduler->setNeedsRedraw(); + scheduler->setNeedsForcedRedraw(); + EXPECT_TRUE(client.hasAction("scheduledActionDrawAndSwapForced")); + + // We should not have told the frame rate controller that we began a frame. + EXPECT_EQ(0, controllerPtr->numFramesPending()); +} + } diff --git a/Source/WebKit/chromium/tests/CCSchedulerTestCommon.h b/Source/WebKit/chromium/tests/CCSchedulerTestCommon.h index 9b5e0383e..6257ec888 100644 --- a/Source/WebKit/chromium/tests/CCSchedulerTestCommon.h +++ b/Source/WebKit/chromium/tests/CCSchedulerTestCommon.h @@ -26,6 +26,7 @@ #define CCSchedulerTestCommon_h #include "cc/CCDelayBasedTimeSource.h" +#include "cc/CCFrameRateController.h" #include "cc/CCThread.h" #include <gtest/gtest.h> #include <wtf/OwnPtr.h> @@ -38,7 +39,7 @@ public: void reset() { m_tickCalled = false; } bool tickCalled() const { return m_tickCalled; } - virtual void onTimerTick() { m_tickCalled = true; } + virtual void onTimerTick() OVERRIDE { m_tickCalled = true; } protected: bool m_tickCalled; @@ -61,7 +62,7 @@ public: task->performTask(); } - long long pendingDelay() const + long long pendingDelayMs() const { EXPECT_TRUE(hasPendingTask()); return m_pendingTaskDelay; @@ -89,9 +90,9 @@ public: virtual ~FakeCCTimeSource() { } - virtual void setClient(WebCore::CCTimeSourceClient* client) { m_client = client; } - virtual void setActive(bool b) { m_active = b; } - virtual bool active() const { return m_active; } + virtual void setClient(WebCore::CCTimeSourceClient* client) OVERRIDE { m_client = client; } + virtual void setActive(bool b) OVERRIDE { m_active = b; } + virtual bool active() const OVERRIDE { return m_active; } void tick() { @@ -107,20 +108,27 @@ protected: class FakeCCDelayBasedTimeSource : public WebCore::CCDelayBasedTimeSource { public: - static PassRefPtr<FakeCCDelayBasedTimeSource> create(double intervalMs, WebCore::CCThread* thread) + static PassRefPtr<FakeCCDelayBasedTimeSource> create(double interval, WebCore::CCThread* thread) { - return adoptRef(new FakeCCDelayBasedTimeSource(intervalMs, thread)); + return adoptRef(new FakeCCDelayBasedTimeSource(interval, thread)); } - void setMonotonicallyIncreasingTimeMs(double time) { m_monotonicallyIncreasingTimeMs = time; } - virtual double monotonicallyIncreasingTimeMs() const { return m_monotonicallyIncreasingTimeMs; } + void setMonotonicallyIncreasingTime(double time) { m_monotonicallyIncreasingTime = time; } + virtual double monotonicallyIncreasingTime() const { return m_monotonicallyIncreasingTime; } protected: - FakeCCDelayBasedTimeSource(double intervalMs, WebCore::CCThread* thread) - : CCDelayBasedTimeSource(intervalMs, thread) - , m_monotonicallyIncreasingTimeMs(0) { } + FakeCCDelayBasedTimeSource(double interval, WebCore::CCThread* thread) + : CCDelayBasedTimeSource(interval, thread) + , m_monotonicallyIncreasingTime(0) { } - double m_monotonicallyIncreasingTimeMs; + double m_monotonicallyIncreasingTime; +}; + +class FakeCCFrameRateController : public WebCore::CCFrameRateController { +public: + FakeCCFrameRateController(PassRefPtr<WebCore::CCTimeSource> timer) : WebCore::CCFrameRateController(timer) { } + + int numFramesPending() const { return m_numFramesPending; } }; } diff --git a/Source/WebKit/chromium/tests/CCSolidColorLayerImplTest.cpp b/Source/WebKit/chromium/tests/CCSolidColorLayerImplTest.cpp index 02dc908ab..6b7124975 100644 --- a/Source/WebKit/chromium/tests/CCSolidColorLayerImplTest.cpp +++ b/Source/WebKit/chromium/tests/CCSolidColorLayerImplTest.cpp @@ -27,6 +27,7 @@ #include "cc/CCSolidColorLayerImpl.h" #include "CCLayerTestCommon.h" +#include "MockCCQuadCuller.h" #include "cc/CCSingleThreadProxy.h" #include "cc/CCSolidColorDrawQuad.h" @@ -42,7 +43,7 @@ TEST(CCSolidColorLayerImplTest, verifyTilingCompleteAndNoOverlap) { DebugScopedSetImplThread scopedImplThread; - CCQuadList quadList; + MockCCQuadCuller quadCuller; IntSize layerSize = IntSize(800, 600); IntRect visibleLayerRect = IntRect(IntPoint(), layerSize); @@ -51,9 +52,10 @@ TEST(CCSolidColorLayerImplTest, verifyTilingCompleteAndNoOverlap) layer->setBounds(layerSize); OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quadList, sharedQuadState.get()); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); - verifyQuadsExactlyCoverRect(quadList, visibleLayerRect); + verifyQuadsExactlyCoverRect(quadCuller.quadList(), visibleLayerRect); } TEST(CCSolidColorLayerImplTest, verifyCorrectBackgroundColorInQuad) @@ -62,7 +64,7 @@ TEST(CCSolidColorLayerImplTest, verifyCorrectBackgroundColorInQuad) const Color testColor = 0xFFA55AFF; - CCQuadList quadList; + MockCCQuadCuller quadCuller; IntSize layerSize = IntSize(100, 100); IntRect visibleLayerRect = IntRect(IntPoint(), layerSize); @@ -72,10 +74,34 @@ TEST(CCSolidColorLayerImplTest, verifyCorrectBackgroundColorInQuad) layer->setBackgroundColor(testColor); OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quadList, sharedQuadState.get()); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); - ASSERT_EQ(quadList.size(), 1U); - EXPECT_EQ(quadList[0]->toSolidColorDrawQuad()->color(), testColor); + ASSERT_EQ(quadCuller.quadList().size(), 1U); + EXPECT_EQ(quadCuller.quadList()[0]->toSolidColorDrawQuad()->color(), testColor); +} + +TEST(CCSolidColorLayerImplTest, verifyCorrectOpacityInQuad) +{ + DebugScopedSetImplThread scopedImplThread; + + const float opacity = 0.5f; + + MockCCQuadCuller quadCuller; + IntSize layerSize = IntSize(100, 100); + IntRect visibleLayerRect = IntRect(IntPoint(), layerSize); + + OwnPtr<CCSolidColorLayerImpl> layer = CCSolidColorLayerImpl::create(0); + layer->setVisibleLayerRect(visibleLayerRect); + layer->setBounds(layerSize); + layer->setDrawOpacity(opacity); + + OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + + ASSERT_EQ(quadCuller.quadList().size(), 1U); + EXPECT_EQ(opacity, quadCuller.quadList()[0]->toSolidColorDrawQuad()->opacity()); } } // namespace diff --git a/Source/WebKit/chromium/tests/CCTiledLayerImplTest.cpp b/Source/WebKit/chromium/tests/CCTiledLayerImplTest.cpp index ff7daa3c9..48a46dae1 100644 --- a/Source/WebKit/chromium/tests/CCTiledLayerImplTest.cpp +++ b/Source/WebKit/chromium/tests/CCTiledLayerImplTest.cpp @@ -27,6 +27,7 @@ #include "cc/CCTiledLayerImpl.h" #include "CCLayerTestCommon.h" +#include "MockCCQuadCuller.h" #include "cc/CCSingleThreadProxy.h" #include "cc/CCTileDrawQuad.h" #include <gmock/gmock.h> @@ -69,11 +70,12 @@ TEST(CCTiledLayerImplTest, emptyQuadList) // Verify default layer does creates quads { OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels); - CCQuadList quads; + MockCCQuadCuller quadCuller; OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quads, sharedQuadState.get()); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); const unsigned numTiles = numTilesX * numTilesY; - EXPECT_EQ(quads.size(), numTiles); + EXPECT_EQ(quadCuller.quadList().size(), numTiles); } // Layer with empty visible layer rect produces no quads @@ -81,10 +83,11 @@ TEST(CCTiledLayerImplTest, emptyQuadList) OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels); layer->setVisibleLayerRect(IntRect()); - CCQuadList quads; + MockCCQuadCuller quadCuller; OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quads, sharedQuadState.get()); - EXPECT_EQ(quads.size(), 0u); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + EXPECT_EQ(quadCuller.quadList().size(), 0u); } // Layer with non-intersecting visible layer rect produces no quads @@ -94,10 +97,11 @@ TEST(CCTiledLayerImplTest, emptyQuadList) IntRect outsideBounds(IntPoint(-100, -100), IntSize(50, 50)); layer->setVisibleLayerRect(outsideBounds); - CCQuadList quads; + MockCCQuadCuller quadCuller; OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quads, sharedQuadState.get()); - EXPECT_EQ(quads.size(), 0u); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + EXPECT_EQ(quadCuller.quadList().size(), 0u); } // Layer with skips draw produces no quads @@ -105,10 +109,11 @@ TEST(CCTiledLayerImplTest, emptyQuadList) OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels); layer->setSkipsDraw(true); - CCQuadList quads; + MockCCQuadCuller quadCuller; OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quads, sharedQuadState.get()); - EXPECT_EQ(quads.size(), 0u); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + EXPECT_EQ(quadCuller.quadList().size(), 0u); } } @@ -126,12 +131,14 @@ TEST(CCTiledLayerImplTest, checkerboarding) // No checkerboarding { - CCQuadList quads; - layer->appendQuads(quads, sharedQuadState.get()); - EXPECT_EQ(quads.size(), 4u); - - for (size_t i = 0; i < quads.size(); ++i) - EXPECT_EQ(quads[i]->material(), CCDrawQuad::TiledContent); + MockCCQuadCuller quadCuller; + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + EXPECT_EQ(quadCuller.quadList().size(), 4u); + EXPECT_FALSE(hadMissingTiles); + + for (size_t i = 0; i < quadCuller.quadList().size(); ++i) + EXPECT_EQ(quadCuller.quadList()[i]->material(), CCDrawQuad::TiledContent); } for (int i = 0; i < numTilesX; ++i) @@ -140,11 +147,13 @@ TEST(CCTiledLayerImplTest, checkerboarding) // All checkerboarding { - CCQuadList quads; - layer->appendQuads(quads, sharedQuadState.get()); - EXPECT_EQ(quads.size(), 4u); - for (size_t i = 0; i < quads.size(); ++i) - EXPECT_EQ(quads[i]->material(), CCDrawQuad::SolidColor); + MockCCQuadCuller quadCuller; + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); + EXPECT_TRUE(hadMissingTiles); + EXPECT_EQ(quadCuller.quadList().size(), 4u); + for (size_t i = 0; i < quadCuller.quadList().size(); ++i) + EXPECT_NE(quadCuller.quadList()[i]->material(), CCDrawQuad::TiledContent); } } @@ -154,8 +163,10 @@ static PassOwnPtr<CCSharedQuadState> getQuads(CCQuadList& quads, IntSize tileSiz layer->setVisibleLayerRect(visibleLayerRect); layer->setBounds(layerSize); + MockCCQuadCuller quadCuller(quads); OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - layer->appendQuads(quads, sharedQuadState.get()); + bool hadMissingTiles = false; + layer->appendQuads(quadCuller, sharedQuadState.get(), hadMissingTiles); return sharedQuadState.release(); // The shared data must be owned as long as the quad list exists. } @@ -251,73 +262,4 @@ TEST(CCTiledLayerImplTest, tileOpaqueRectForLayerNoBorders) } } -TEST(CCTiledLayerImplTest, backgroundCoversViewport) -{ - DebugScopedSetImplThread scopedImplThread; - - const IntSize tileSize(10, 10); - const int numTilesX = 2; - const int numTilesY = 2; - const unsigned numTiles = numTilesX * numTilesY; - const IntSize layerSize(tileSize.width() * numTilesX, tileSize.height() * numTilesY); - OwnPtr<CCTiledLayerImpl> layer = createLayer(tileSize, layerSize, CCLayerTilingData::NoBorderTexels); - layer->setBackgroundColor(Color::gray); - layer->setBackgroundCoversViewport(true); - - // No gutter rects - { - IntRect clipRect = IntRect(IntPoint(), layerSize); - layer->setClipRect(clipRect); - layer->setVisibleLayerRect(IntRect(IntPoint(), layerSize)); - - OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - - CCQuadList quads; - layer->appendQuads(quads, sharedQuadState.get()); - EXPECT_EQ(quads.size(), numTiles); - - for (size_t i = 0; i < quads.size(); ++i) - EXPECT_EQ(quads[i]->material(), CCDrawQuad::TiledContent); - } - - // Empty visible content area (fullscreen gutter rect) - { - IntRect clipRect = IntRect(100, 100, 100, 100); - layer->setClipRect(clipRect); - layer->setVisibleLayerRect(IntRect()); - - OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - CCQuadList quads; - layer->appendQuads(quads, sharedQuadState.get()); - - for (size_t i = 0; i < quads.size(); ++i) - EXPECT_EQ(quads[i]->material(), CCDrawQuad::SolidColor); - - verifyQuadsExactlyCoverRect(quads, clipRect); - } - - // Content area in middle of clip rect (four surrounding gutter rects) - { - IntRect clipRect = IntRect(-50, -50, 100, 100); - layer->setClipRect(clipRect); - layer->setVisibleLayerRect(IntRect(IntPoint(), layerSize)); - - OwnPtr<CCSharedQuadState> sharedQuadState = layer->createSharedQuadState(); - CCQuadList quads; - layer->appendQuads(quads, sharedQuadState.get()); - - unsigned numContentTiles = 0, numGutterTiles = 0; - for (size_t i = 0; i < quads.size(); ++i) { - if (quads[i]->material() == CCDrawQuad::TiledContent) - numContentTiles++; - else if (quads[i]->material() == CCDrawQuad::SolidColor) - numGutterTiles++; - } - EXPECT_EQ(numContentTiles, numTiles); - EXPECT_GE(numGutterTiles, 4u); - - verifyQuadsExactlyCoverRect(quads, clipRect); - } -} - } // namespace diff --git a/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp new file mode 100644 index 000000000..46d11a96e --- /dev/null +++ b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.cpp @@ -0,0 +1,129 @@ +/* + * 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 "CCTiledLayerTestCommon.h" + +using namespace WebCore; + +namespace WebKitTests { + +FakeLayerTextureUpdater::Texture::Texture(FakeLayerTextureUpdater* layer, PassOwnPtr<ManagedTexture> texture) + : LayerTextureUpdater::Texture(texture) + , m_layer(layer) +{ +} + +FakeLayerTextureUpdater::Texture::~Texture() +{ +} + +void FakeLayerTextureUpdater::Texture::updateRect(GraphicsContext3D*, TextureAllocator* allocator, const IntRect&, const IntRect&) +{ + if (allocator) + texture()->allocate(allocator); + m_layer->updateRect(); +} + +void FakeLayerTextureUpdater::Texture::prepareRect(const IntRect&) +{ + m_layer->prepareRect(); +} + +FakeLayerTextureUpdater::FakeLayerTextureUpdater() + : m_prepareCount(0) + , m_updateCount(0) + , m_prepareRectCount(0) +{ +} + +FakeLayerTextureUpdater::~FakeLayerTextureUpdater() +{ +} + +void FakeLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize&, int, float, IntRect& resultingOpaqueRect) +{ + m_prepareCount++; + m_lastUpdateRect = contentRect; + if (!m_rectToInvalidate.isEmpty()) { + m_layer->invalidateRect(m_rectToInvalidate); + m_rectToInvalidate = IntRect(); + m_layer = 0; + } + resultingOpaqueRect = m_opaquePaintRect; +} + +void FakeLayerTextureUpdater::setRectToInvalidate(const IntRect& rect, FakeTiledLayerChromium* layer) +{ + m_rectToInvalidate = rect; + m_layer = layer; +} + +PassOwnPtr<LayerTextureUpdater::Texture> FakeLayerTextureUpdater::createTexture(TextureManager* manager) +{ + return adoptPtr(new Texture(this, ManagedTexture::create(manager))); +} + +FakeCCTiledLayerImpl::FakeCCTiledLayerImpl(int id) + : CCTiledLayerImpl(id) +{ +} + +FakeCCTiledLayerImpl::~FakeCCTiledLayerImpl() +{ +} + +FakeTiledLayerChromium::FakeTiledLayerChromium(TextureManager* textureManager) + : TiledLayerChromium() + , m_fakeTextureUpdater(adoptRef(new FakeLayerTextureUpdater)) + , m_textureManager(textureManager) +{ + setTileSize(tileSize()); + setTextureFormat(GraphicsContext3D::RGBA); + setBorderTexelOption(CCLayerTilingData::NoBorderTexels); + setIsDrawable(true); // So that we don't get false positives if any of these tests expect to return false from drawsContent() for other reasons. +} + +FakeTiledLayerChromium::~FakeTiledLayerChromium() +{ +} + +void FakeTiledLayerChromium::setNeedsDisplayRect(const FloatRect& rect) +{ + m_lastNeedsDisplayRect = rect; + TiledLayerChromium::setNeedsDisplayRect(rect); +} + +void FakeTiledLayerChromium::update(CCTextureUpdater& updater, const CCOcclusionTracker* occlusion) +{ + updateLayerRect(updater, visibleLayerRect(), occlusion); +} + +FakeTiledLayerWithScaledBounds::FakeTiledLayerWithScaledBounds(TextureManager* textureManager) + : FakeTiledLayerChromium(textureManager) +{ +} + +} // namespace diff --git a/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h new file mode 100644 index 000000000..c028ea725 --- /dev/null +++ b/Source/WebKit/chromium/tests/CCTiledLayerTestCommon.h @@ -0,0 +1,168 @@ +/* + * 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. + */ + +#ifndef CCTiledLayerTestCommon_h +#define CCTiledLayerTestCommon_h + +#include "GraphicsContext3D.h" +#include "IntRect.h" +#include "IntSize.h" +#include "LayerTextureUpdater.h" +#include "Region.h" +#include "TextureCopier.h" +#include "TextureManager.h" +#include "TextureUploader.h" +#include "TiledLayerChromium.h" +#include "cc/CCTextureUpdater.h" +#include "cc/CCTiledLayerImpl.h" + +namespace WebKitTests { + +class FakeTiledLayerChromium; + +class FakeLayerTextureUpdater : public WebCore::LayerTextureUpdater { +public: + class Texture : public WebCore::LayerTextureUpdater::Texture { + public: + Texture(FakeLayerTextureUpdater*, PassOwnPtr<WebCore::ManagedTexture>); + virtual ~Texture(); + + virtual void updateRect(WebCore::GraphicsContext3D*, WebCore::TextureAllocator* , const WebCore::IntRect&, const WebCore::IntRect&); + virtual void prepareRect(const WebCore::IntRect&); + + private: + FakeLayerTextureUpdater* m_layer; + }; + + FakeLayerTextureUpdater(); + virtual ~FakeLayerTextureUpdater(); + + virtual PassOwnPtr<WebCore::LayerTextureUpdater::Texture> createTexture(WebCore::TextureManager*); + virtual SampledTexelFormat sampledTexelFormat(GC3Denum) { return SampledTexelFormatRGBA; } + + virtual void prepareToUpdate(const WebCore::IntRect& contentRect, const WebCore::IntSize&, int, float, WebCore::IntRect& resultingOpaqueRect); + // Sets the rect to invalidate during the next call to prepareToUpdate(). After the next + // call to prepareToUpdate() the rect is reset. + void setRectToInvalidate(const WebCore::IntRect&, FakeTiledLayerChromium*); + // Last rect passed to prepareToUpdate(). + const WebCore::IntRect& lastUpdateRect() const { return m_lastUpdateRect; } + + // Number of times prepareToUpdate has been invoked. + int prepareCount() const { return m_prepareCount; } + void clearPrepareCount() { m_prepareCount = 0; } + + // Number of times updateRect has been invoked. + int updateCount() const { return m_updateCount; } + void clearUpdateCount() { m_updateCount = 0; } + void updateRect() { m_updateCount++; } + + // Number of times prepareRect() has been invoked on a texture. + int prepareRectCount() const { return m_prepareRectCount; } + void clearPrepareRectCount() { m_prepareRectCount = 0; } + void prepareRect() { m_prepareRectCount++; } + + void setOpaquePaintRect(const WebCore::IntRect& opaquePaintRect) { m_opaquePaintRect = opaquePaintRect; } + +private: + int m_prepareCount; + int m_updateCount; + int m_prepareRectCount; + WebCore::IntRect m_rectToInvalidate; + WebCore::IntRect m_lastUpdateRect; + WebCore::IntRect m_opaquePaintRect; + RefPtr<FakeTiledLayerChromium> m_layer; +}; + +class FakeCCTiledLayerImpl : public WebCore::CCTiledLayerImpl { +public: + explicit FakeCCTiledLayerImpl(int id); + virtual ~FakeCCTiledLayerImpl(); + + using WebCore::CCTiledLayerImpl::hasTileAt; + using WebCore::CCTiledLayerImpl::hasTextureIdForTileAt; +}; + +class FakeTiledLayerChromium : public WebCore::TiledLayerChromium { +public: + explicit FakeTiledLayerChromium(WebCore::TextureManager*); + virtual ~FakeTiledLayerChromium(); + + static WebCore::IntSize tileSize() { return WebCore::IntSize(100, 100); } + + using WebCore::TiledLayerChromium::invalidateRect; + using WebCore::TiledLayerChromium::updateLayerRect; + using WebCore::TiledLayerChromium::idleUpdateLayerRect; + using WebCore::TiledLayerChromium::needsIdlePaint; + using WebCore::TiledLayerChromium::skipsDraw; + using WebCore::TiledLayerChromium::numPaintedTiles; + using WebCore::TiledLayerChromium::idlePaintRect; + + virtual void setNeedsDisplayRect(const WebCore::FloatRect&) OVERRIDE; + const WebCore::FloatRect& lastNeedsDisplayRect() const { return m_lastNeedsDisplayRect; } + + // Updates the visibleLayerRect(). + virtual void update(WebCore::CCTextureUpdater&, const WebCore::CCOcclusionTracker*) OVERRIDE; + + virtual WebCore::TextureManager* textureManager() const OVERRIDE { return m_textureManager; } + FakeLayerTextureUpdater* fakeLayerTextureUpdater() { return m_fakeTextureUpdater.get(); } + WebCore::FloatRect updateRect() { return m_updateRect; } + +private: + virtual WebCore::LayerTextureUpdater* textureUpdater() const OVERRIDE { return m_fakeTextureUpdater.get(); } + virtual void createTextureUpdaterIfNeeded() OVERRIDE { } + + RefPtr<FakeLayerTextureUpdater> m_fakeTextureUpdater; + WebCore::TextureManager* m_textureManager; + WebCore::FloatRect m_lastNeedsDisplayRect; +}; + +class FakeTiledLayerWithScaledBounds : public FakeTiledLayerChromium { +public: + explicit FakeTiledLayerWithScaledBounds(WebCore::TextureManager*); + + void setContentBounds(const WebCore::IntSize& contentBounds) { m_forcedContentBounds = contentBounds; } + virtual WebCore::IntSize contentBounds() const OVERRIDE { return m_forcedContentBounds; } + +protected: + WebCore::IntSize m_forcedContentBounds; +}; + +class FakeTextureAllocator : public WebCore::TextureAllocator { +public: + virtual unsigned createTexture(const WebCore::IntSize&, GC3Denum) { return 1; } + virtual void deleteTexture(unsigned, const WebCore::IntSize&, GC3Denum) { } +}; + +class FakeTextureCopier : public WebCore::TextureCopier { +public: + virtual void copyTexture(WebCore::GraphicsContext3D*, unsigned, unsigned, const WebCore::IntSize&) { } +}; + +class FakeTextureUploader : public WebCore::TextureUploader { +public: + virtual void uploadTexture(WebCore::GraphicsContext3D* context, WebCore::LayerTextureUpdater::Texture* texture, WebCore::TextureAllocator* allocator, const WebCore::IntRect sourceRect, const WebCore::IntRect destRect) { texture->updateRect(context, allocator, sourceRect, destRect); } +}; + +} +#endif // CCTiledLayerTestCommon_h diff --git a/Source/WebKit/chromium/tests/CCTimerTest.cpp b/Source/WebKit/chromium/tests/CCTimerTest.cpp index 298b69562..398fca56e 100644 --- a/Source/WebKit/chromium/tests/CCTimerTest.cpp +++ b/Source/WebKit/chromium/tests/CCTimerTest.cpp @@ -54,7 +54,7 @@ protected: TEST_F(CCTimerTest, OneShot) { CCTimer timer(&m_thread, this); - timer.startOneShot(1); + timer.startOneShot(0.001); m_thread.runPendingTask(); EXPECT_TRUE(m_flag); @@ -64,7 +64,7 @@ TEST_F(CCTimerTest, OneShot) TEST_F(CCTimerTest, StopManually) { CCTimer timer(&m_thread, this); - timer.startOneShot(1); + timer.startOneShot(0.001); timer.stop(); m_thread.runPendingTask(); @@ -76,7 +76,7 @@ TEST_F(CCTimerTest, StopByScope) { { CCTimer timer(&m_thread, this); - timer.startOneShot(1); + timer.startOneShot(0.001); } m_thread.runPendingTask(); diff --git a/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp index 580f15791..f464dcbbc 100644 --- a/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/Canvas2DLayerChromiumTest.cpp @@ -27,13 +27,19 @@ #include "Canvas2DLayerChromium.h" #include "CCSchedulerTestCommon.h" +#include "FakeCCLayerTreeHostClient.h" #include "FakeWebGraphicsContext3D.h" #include "GraphicsContext3DPrivate.h" #include "Region.h" +#include "TextureCopier.h" #include "TextureManager.h" -#include "cc/CCCanvasLayerImpl.h" +#include "WebCompositor.h" +#include "WebKit.h" #include "cc/CCSingleThreadProxy.h" +#include "cc/CCTextureLayerImpl.h" #include "cc/CCTextureUpdater.h" +#include "platform/WebKitPlatformSupport.h" +#include "platform/WebThread.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -48,19 +54,27 @@ using testing::Test; namespace { -class MockCanvasContext : public FakeWebGraphicsContext3D { +class FakeCCLayerTreeHost : public CCLayerTreeHost { public: - MOCK_METHOD0(createFramebuffer, WebGLId()); - MOCK_METHOD0(createTexture, WebGLId()); + static PassOwnPtr<FakeCCLayerTreeHost> create() + { + OwnPtr<FakeCCLayerTreeHost> host(adoptPtr(new FakeCCLayerTreeHost)); + host->initialize(); + return host.release(); + } - MOCK_METHOD2(bindFramebuffer, void(WGC3Denum, WebGLId)); - MOCK_METHOD5(framebufferTexture2D, void(WGC3Denum, WGC3Denum, WGC3Denum, WebGLId, WGC3Dint)); +private: + FakeCCLayerTreeHost() + : CCLayerTreeHost(&m_client, CCSettings()) + { + } - MOCK_METHOD2(bindTexture, void(WGC3Denum, WebGLId)); - MOCK_METHOD8(copyTexSubImage2D, void(WGC3Denum, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dsizei, WGC3Dsizei)); + FakeCCLayerTreeHostClient m_client; +}; - MOCK_METHOD1(deleteFramebuffer, void(WebGLId)); - MOCK_METHOD1(deleteTexture, void(WebGLId)); +class MockCanvasContext : public FakeWebGraphicsContext3D { +public: + MOCK_METHOD0(flush, void(void)); }; class MockTextureAllocator : public TextureAllocator { @@ -69,13 +83,21 @@ public: MOCK_METHOD3(deleteTexture, void(unsigned, const IntSize&, GC3Denum)); }; -} // namespace +class MockTextureCopier : public TextureCopier { +public: + MOCK_METHOD4(copyTexture, void(GraphicsContext3D*, unsigned, unsigned, const IntSize&)); +}; + +class MockTextureUploader : public TextureUploader { +public: + MOCK_METHOD5(uploadTexture, void(GraphicsContext3D*, LayerTextureUpdater::Texture*, TextureAllocator*, const IntRect, const IntRect)); +}; -namespace WebCore { +} // namespace class Canvas2DLayerChromiumTest : public Test { protected: - void fullLifecycleTest(bool threaded) + void fullLifecycleTest(bool threaded, bool deferred) { GraphicsContext3D::Attributes attrs; @@ -86,84 +108,95 @@ protected: MockCanvasContext& implMock = *static_cast<MockCanvasContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(implContext.get())); MockTextureAllocator allocatorMock; - CCTextureUpdater updater(&allocatorMock); + MockTextureCopier copierMock; + MockTextureUploader uploaderMock; + CCTextureUpdater updater; const IntSize size(300, 150); - const size_t maxTextureSize = size.width() * size.height() * 4; - OwnPtr<TextureManager> textureManager = TextureManager::create(maxTextureSize, maxTextureSize, maxTextureSize); + OwnPtr<WebThread> thread; if (threaded) - CCProxy::setImplThread(new FakeCCThread); + thread = adoptPtr(WebKit::Platform::current()->createThread("Canvas2DLayerChromiumTest")); + WebCompositor::initialize(thread.get()); + + OwnPtr<FakeCCLayerTreeHost> layerTreeHost(FakeCCLayerTreeHost::create()); + // Force an update, so that we get a valid TextureManager. + layerTreeHost->updateLayers(updater); const WebGLId backTextureId = 1; const WebGLId frontTextureId = 2; - const WebGLId fboId = 3; { InSequence sequence; - // Note that the canvas backing texture is doublebuffered only when using the threaded - // compositor. - if (threaded) { - // Setup Canvas2DLayerChromium (on the main thread). - EXPECT_CALL(mainMock, createFramebuffer()) - .WillOnce(Return(fboId)); + // Paint canvas contents on the main thread. + EXPECT_CALL(mainMock, flush()); + // Note that the canvas backing texture is doublebuffered only when using the threaded + // compositor and not using deferred canvas rendering + if (threaded && !deferred) { // Create texture and do the copy (on the impl thread). EXPECT_CALL(allocatorMock, createTexture(size, GraphicsContext3D::RGBA)) .WillOnce(Return(frontTextureId)); - EXPECT_CALL(implMock, bindTexture(GraphicsContext3D::TEXTURE_2D, frontTextureId)); - EXPECT_CALL(implMock, bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, fboId)); - EXPECT_CALL(implMock, framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, backTextureId, 0)); - EXPECT_CALL(implMock, copyTexSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, 0, 0, 300, 150)); - EXPECT_CALL(implMock, bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0)); - - // Teardown Canvas2DLayerChromium. - EXPECT_CALL(mainMock, deleteFramebuffer(fboId)); + EXPECT_CALL(copierMock, copyTexture(implContext.get(), backTextureId, frontTextureId, size)); + EXPECT_CALL(implMock, flush()); // Teardown TextureManager. EXPECT_CALL(allocatorMock, deleteTexture(frontTextureId, size, GraphicsContext3D::RGBA)); } } - RefPtr<Canvas2DLayerChromium> canvas = Canvas2DLayerChromium::create(mainContext.get(), size); + RefPtr<Canvas2DLayerChromium> canvas = Canvas2DLayerChromium::create(mainContext.get(), size, deferred ? Deferred : NonDeferred); canvas->setIsDrawable(true); - canvas->setTextureManager(textureManager.get()); + canvas->setLayerTreeHost(layerTreeHost.get()); canvas->setBounds(IntSize(600, 300)); canvas->setTextureId(backTextureId); canvas->setNeedsDisplay(); EXPECT_TRUE(canvas->needsDisplay()); - Region occludedScreenSpace; - canvas->paintContentsIfDirty(occludedScreenSpace); + canvas->update(updater, 0); EXPECT_FALSE(canvas->needsDisplay()); { DebugScopedSetImplThread scopedImplThread; OwnPtr<CCLayerImpl> layerImpl = canvas->createCCLayerImpl(); - EXPECT_EQ(0u, static_cast<CCCanvasLayerImpl*>(layerImpl.get())->textureId()); + EXPECT_EQ(0u, static_cast<CCTextureLayerImpl*>(layerImpl.get())->textureId()); - canvas->updateCompositorResources(implContext.get(), updater); + updater.update(implContext.get(), &allocatorMock, &copierMock, &uploaderMock, 1); canvas->pushPropertiesTo(layerImpl.get()); - if (threaded) - EXPECT_EQ(frontTextureId, static_cast<CCCanvasLayerImpl*>(layerImpl.get())->textureId()); + if (threaded && !deferred) + EXPECT_EQ(frontTextureId, static_cast<CCTextureLayerImpl*>(layerImpl.get())->textureId()); else - EXPECT_EQ(backTextureId, static_cast<CCCanvasLayerImpl*>(layerImpl.get())->textureId()); + EXPECT_EQ(backTextureId, static_cast<CCTextureLayerImpl*>(layerImpl.get())->textureId()); } canvas.clear(); - textureManager->reduceMemoryToLimit(0); - textureManager->deleteEvictedTextures(&allocatorMock); + layerTreeHost->contentsTextureManager()->reduceMemoryToLimit(0); + layerTreeHost->contentsTextureManager()->deleteEvictedTextures(&allocatorMock); + layerTreeHost.clear(); + WebCompositor::shutdown(); } }; +namespace { + TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleSingleThread) { - fullLifecycleTest(false); + fullLifecycleTest(false, false); } TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleThreaded) { - fullLifecycleTest(true); + fullLifecycleTest(true, false); } -} // namespace webcore +TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleSingleThreadDeferred) +{ + fullLifecycleTest(false, true); +} + +TEST_F(Canvas2DLayerChromiumTest, testFullLifecycleThreadedDeferred) +{ + fullLifecycleTest(true, true); +} + +} // namespace diff --git a/Source/WebKit/chromium/tests/WebGLLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/DrawingBufferChromiumTest.cpp index 66ea690fa..27ef49281 100644 --- a/Source/WebKit/chromium/tests/WebGLLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/DrawingBufferChromiumTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * 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 @@ -24,10 +24,10 @@ #include "config.h" -#include "WebGLLayerChromium.h" +#include "DrawingBuffer.h" #include "CompositorFakeGraphicsContext3D.h" -#include "DrawingBuffer.h" +#include "LayerChromium.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -36,7 +36,7 @@ using namespace WebKit; namespace { -TEST(WebGLLayerChromiumTest, opaqueFormats) +TEST(DrawingBufferChromiumTest, opaqueFormats) { RefPtr<DrawingBuffer> buffer; @@ -50,14 +50,14 @@ TEST(WebGLLayerChromiumTest, opaqueFormats) RefPtr<GraphicsContext3D> opaqueContext = createCompositorMockGraphicsContext3D(opaqueAttrs); EXPECT_TRUE(opaqueContext); - buffer = DrawingBuffer::create(alphaContext.get(), IntSize(), false); + buffer = DrawingBuffer::create(alphaContext.get(), IntSize(), DrawingBuffer::Preserve, DrawingBuffer::Alpha); EXPECT_FALSE(buffer->platformLayer()->opaque()); - buffer = DrawingBuffer::create(alphaContext.get(), IntSize(), true); + buffer = DrawingBuffer::create(alphaContext.get(), IntSize(), DrawingBuffer::Discard, DrawingBuffer::Alpha); EXPECT_FALSE(buffer->platformLayer()->opaque()); - buffer = DrawingBuffer::create(opaqueContext.get(), IntSize(), false); + buffer = DrawingBuffer::create(opaqueContext.get(), IntSize(), DrawingBuffer::Preserve, DrawingBuffer::Opaque); EXPECT_TRUE(buffer->platformLayer()->opaque()); - buffer = DrawingBuffer::create(opaqueContext.get(), IntSize(), true); + buffer = DrawingBuffer::create(opaqueContext.get(), IntSize(), DrawingBuffer::Discard, DrawingBuffer::Opaque); EXPECT_TRUE(buffer->platformLayer()->opaque()); } diff --git a/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h b/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h index 38876d737..513fd0cbc 100755 --- a/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h +++ b/Source/WebKit/chromium/tests/FakeCCLayerTreeHostClient.h @@ -34,20 +34,22 @@ namespace WebCore { class FakeCCLayerTreeHostClient : public CCLayerTreeHostClient { public: - virtual void updateAnimations(double frameBeginTime) { } - virtual void layout() { } - virtual void applyScrollAndScale(const IntSize& scrollDelta, float pageScale) { } - virtual PassRefPtr<GraphicsContext3D> createContext() + virtual void willBeginFrame() OVERRIDE { } + virtual void updateAnimations(double monotonicFrameBeginTime) OVERRIDE { } + virtual void layout() OVERRIDE { } + virtual void applyScrollAndScale(const IntSize& scrollDelta, float pageScale) OVERRIDE { } + virtual PassRefPtr<GraphicsContext3D> createContext() OVERRIDE { GraphicsContext3D::Attributes attrs; return createCompositorMockGraphicsContext3D(attrs); } - virtual void didRecreateContext(bool success) { } - virtual void didCommitAndDrawFrame() { } - virtual void didCompleteSwapBuffers() { } + virtual void didRecreateContext(bool success) OVERRIDE { } + virtual void didCommit() OVERRIDE { } + virtual void didCommitAndDrawFrame() OVERRIDE { } + virtual void didCompleteSwapBuffers() OVERRIDE { } // Used only in the single-threaded path. - virtual void scheduleComposite() { } + virtual void scheduleComposite() OVERRIDE { } }; } diff --git a/Source/WebKit/chromium/tests/FakeWebGraphicsContext3D.h b/Source/WebKit/chromium/tests/FakeWebGraphicsContext3D.h index 268dc1da3..191d05f5f 100644 --- a/Source/WebKit/chromium/tests/FakeWebGraphicsContext3D.h +++ b/Source/WebKit/chromium/tests/FakeWebGraphicsContext3D.h @@ -63,6 +63,9 @@ public: virtual void setVisibilityCHROMIUM(bool visible) { } + virtual void discardFramebufferEXT(WGC3Denum target, WGC3Dsizei numAttachments, const WGC3Denum* attachments) { } + virtual void ensureFramebufferCHROMIUM() { } + virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { } virtual WebString getRequestableExtensionsCHROMIUM() { return WebString(); } @@ -155,7 +158,7 @@ public: } virtual WebString getShaderInfoLog(WebGLId shader) { return WebString(); } - + virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precisiontype, WGC3Dint* range, WGC3Dint* precision) { } virtual WebString getShaderSource(WebGLId shader) { return WebString(); } virtual WebString getString(WGC3Denum name) { return WebString(); } virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat* value) { } @@ -255,6 +258,14 @@ public: virtual void texStorage2DEXT(WGC3Denum target, WGC3Dint levels, WGC3Duint internalformat, WGC3Dint width, WGC3Dint height) { } + virtual WebGLId createQueryEXT() { return 1; } + virtual void deleteQueryEXT(WebGLId) { } + virtual GC3Dboolean isQueryEXT(WebGLId) { return true; } + virtual void beginQueryEXT(GC3Denum, WebGLId) { } + virtual void endQueryEXT(GC3Denum) { } + virtual void getQueryivEXT(GC3Denum, GC3Denum, GC3Dint*) { } + virtual void getQueryObjectuivEXT(WebGLId, GC3Denum, GC3Duint*) { } + protected: Attributes m_attrs; }; diff --git a/Source/WebKit/chromium/tests/FloatQuadTest.cpp b/Source/WebKit/chromium/tests/FloatQuadTest.cpp index 7633dcf15..6c7091ca4 100644 --- a/Source/WebKit/chromium/tests/FloatQuadTest.cpp +++ b/Source/WebKit/chromium/tests/FloatQuadTest.cpp @@ -50,7 +50,7 @@ TEST(FloatQuadTest, IsRectilinearTest) rectilinearTrans[7].rotate(180); for (int i = 0; i < numRectilinear; ++i) { - FloatQuad quad = rectilinearTrans[i].mapQuad(FloatRect(0.01010101, 0.01010101, 100.01010101, 100.01010101)); + FloatQuad quad = rectilinearTrans[i].mapQuad(FloatRect(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)); EXPECT_TRUE(quad.isRectilinear()); } @@ -68,7 +68,7 @@ TEST(FloatQuadTest, IsRectilinearTest) nonRectilinearTrans[9].skewY(0.00001); for (int i = 0; i < numNonRectilinear; ++i) { - FloatQuad quad = nonRectilinearTrans[i].mapQuad(FloatRect(0.01010101, 0.01010101, 100.01010101, 100.01010101)); + FloatQuad quad = nonRectilinearTrans[i].mapQuad(FloatRect(0.01010101f, 0.01010101f, 100.01010101f, 100.01010101f)); EXPECT_FALSE(quad.isRectilinear()); } } diff --git a/Source/WebKit/chromium/tests/FrameTestHelpers.cpp b/Source/WebKit/chromium/tests/FrameTestHelpers.cpp index ba6666699..e406413dd 100644 --- a/Source/WebKit/chromium/tests/FrameTestHelpers.cpp +++ b/Source/WebKit/chromium/tests/FrameTestHelpers.cpp @@ -31,7 +31,7 @@ #include "config.h" #include "FrameTestHelpers.h" -#include "StdLibExtras.h" +#include <wtf/StdLibExtras.h> #include "WebFrame.h" #include "WebFrameClient.h" #include "WebSettings.h" @@ -48,6 +48,11 @@ namespace FrameTestHelpers { void registerMockedURLLoad(const std::string& base, const std::string& fileName) { + registerMockedURLLoad(GURL(base + fileName), fileName); +} + +void registerMockedURLLoad(GURL url, const std::string& fileName) +{ WebURLResponse response; response.initialize(); response.setMIMEType("text/html"); @@ -56,7 +61,7 @@ void registerMockedURLLoad(const std::string& base, const std::string& fileName) filePath += "/Source/WebKit/chromium/tests/data/"; filePath += fileName; - webkit_support::RegisterMockedURL(GURL(base + fileName), response, WebString::fromUTF8(filePath)); + webkit_support::RegisterMockedURL(url, response, WebString::fromUTF8(filePath)); } void loadFrame(WebFrame* frame, const std::string& url) diff --git a/Source/WebKit/chromium/tests/FrameTestHelpers.h b/Source/WebKit/chromium/tests/FrameTestHelpers.h index 290acfe13..fc2fa2d36 100644 --- a/Source/WebKit/chromium/tests/FrameTestHelpers.h +++ b/Source/WebKit/chromium/tests/FrameTestHelpers.h @@ -33,6 +33,8 @@ #include <string> +class GURL; + namespace WebKit { class WebFrame; @@ -43,6 +45,8 @@ class WebViewClient; namespace FrameTestHelpers { void registerMockedURLLoad(const std::string& base, const std::string& fileName); +// Like the previous overload, but it allows more flexibility in the url since it is given by the caller. +void registerMockedURLLoad(GURL, const std::string& fileName); void loadFrame(WebFrame*, const std::string& url); diff --git a/Source/WebKit/chromium/tests/LayerChromiumTest.cpp b/Source/WebKit/chromium/tests/LayerChromiumTest.cpp index d8858cc12..a93eac7fa 100644 --- a/Source/WebKit/chromium/tests/LayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/LayerChromiumTest.cpp @@ -26,13 +26,14 @@ #include "LayerChromium.h" -#include "cc/CCLayerTreeHost.h" #include "CCLayerTreeTestCommon.h" #include "FakeCCLayerTreeHostClient.h" #include "LayerPainterChromium.h" #include "NonCompositedContentHost.h" #include "WebCompositor.h" +#include "cc/CCLayerImpl.h" #include "cc/CCLayerTreeHost.h" +#include "cc/CCSingleThreadProxy.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -77,7 +78,7 @@ protected: { // Initialize without threading support. WebKit::WebCompositor::initialize(0); - m_layerTreeHost = adoptRef(new MockCCLayerTreeHost); + m_layerTreeHost = adoptPtr(new MockCCLayerTreeHost); } virtual void TearDown() @@ -145,7 +146,7 @@ protected: verifyTestTreeInitialState(); } - RefPtr<MockCCLayerTreeHost> m_layerTreeHost; + OwnPtr<MockCCLayerTreeHost> m_layerTreeHost; RefPtr<LayerChromium> m_parent, m_child1, m_child2, m_child3, m_grandChild1, m_grandChild2, m_grandChild3; }; @@ -504,8 +505,7 @@ TEST_F(LayerChromiumTest, checkPropertyChangeCausesCorrectBehavior) // All properties need to be set to new values in order for setNeedsCommit to be called. EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setAnchorPoint(FloatPoint(1.23f, 4.56f))); EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setAnchorPointZ(0.7f)); - EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setBackgroundColor(Color(0.4f, 0.4f, 0.4f))); - EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setBackgroundCoversViewport(true)); + EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setBackgroundColor(Color(0.4f, 0.4f, 0.4f, 1.0f))); EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setMasksToBounds(true)); EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setMaskLayer(dummyLayer.get())); EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setOpacity(0.5f)); @@ -521,6 +521,7 @@ TEST_F(LayerChromiumTest, checkPropertyChangeCausesCorrectBehavior) EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setTransform(TransformationMatrix(0, 0, 0, 0, 0, 0))); EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setDoubleSided(false)); EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setDebugName("Test Layer")); + EXECUTE_AND_VERIFY_SET_NEEDS_COMMIT_BEHAVIOR(1, testLayer->setDrawCheckerboardForMissingTiles(!testLayer->drawCheckerboardForMissingTiles())); // The above tests should not have caused a change to the needsDisplay flag. EXPECT_FALSE(testLayer->needsDisplay()); @@ -530,6 +531,29 @@ TEST_F(LayerChromiumTest, checkPropertyChangeCausesCorrectBehavior) EXPECT_TRUE(testLayer->needsDisplay()); } +TEST_F(LayerChromiumTest, verifyPushPropertiesAccumulatesUpdateRect) +{ + DebugScopedSetImplThread setImplThread; + + RefPtr<LayerChromium> testLayer = LayerChromium::create(); + OwnPtr<CCLayerImpl> implLayer = CCLayerImpl::create(1); + + testLayer->setNeedsDisplayRect(FloatRect(FloatPoint::zero(), FloatSize(5, 5))); + testLayer->pushPropertiesTo(implLayer.get()); + EXPECT_FLOAT_RECT_EQ(FloatRect(FloatPoint::zero(), FloatSize(5, 5)), implLayer->updateRect()); + + // The CCLayerImpl's updateRect should be accumulated here, since we did not do anything to clear it. + testLayer->setNeedsDisplayRect(FloatRect(FloatPoint(10, 10), FloatSize(5, 5))); + testLayer->pushPropertiesTo(implLayer.get()); + EXPECT_FLOAT_RECT_EQ(FloatRect(FloatPoint::zero(), FloatSize(15, 15)), implLayer->updateRect()); + + // If we do clear the CCLayerImpl side, then the next updateRect should be fresh without accumulation. + implLayer->resetAllChangeTrackingForSubtree(); + testLayer->setNeedsDisplayRect(FloatRect(FloatPoint(10, 10), FloatSize(5, 5))); + testLayer->pushPropertiesTo(implLayer.get()); + EXPECT_FLOAT_RECT_EQ(FloatRect(FloatPoint(10, 10), FloatSize(5, 5)), implLayer->updateRect()); +} + class LayerChromiumWithContentScaling : public LayerChromium { public: explicit LayerChromiumWithContentScaling() @@ -537,12 +561,12 @@ public: { } - virtual bool needsContentsScale() const + virtual bool needsContentsScale() const OVERRIDE { return true; } - virtual void setNeedsDisplayRect(const FloatRect& dirtyRect) + virtual void setNeedsDisplayRect(const FloatRect& dirtyRect) OVERRIDE { m_lastNeedsDisplayRect = dirtyRect; LayerChromium::setNeedsDisplayRect(dirtyRect); @@ -577,9 +601,9 @@ TEST_F(LayerChromiumTest, checkContentsScaleChangeTriggersNeedsDisplay) class FakeCCLayerTreeHost : public CCLayerTreeHost { public: - static PassRefPtr<FakeCCLayerTreeHost> create() + static PassOwnPtr<FakeCCLayerTreeHost> create() { - RefPtr<FakeCCLayerTreeHost> host = adoptRef(new FakeCCLayerTreeHost); + OwnPtr<FakeCCLayerTreeHost> host(adoptPtr(new FakeCCLayerTreeHost)); // The initialize call will fail, since our client doesn't provide a valid GraphicsContext3D, but it doesn't matter in the tests that use this fake so ignore the return value. host->initialize(); return host.release(); @@ -626,7 +650,7 @@ TEST(LayerChromiumLayerTreeHostTest, enteringTree) assertLayerTreeHostMatchesForSubtree(parent.get(), 0); - RefPtr<FakeCCLayerTreeHost> layerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> layerTreeHost(FakeCCLayerTreeHost::create()); // Setting the root layer should set the host pointer for all layers in the tree. layerTreeHost->setRootLayer(parent.get()); @@ -645,7 +669,7 @@ TEST(LayerChromiumLayerTreeHostTest, addingLayerSubtree) { WebKit::WebCompositor::initialize(0); RefPtr<LayerChromium> parent = LayerChromium::create(); - RefPtr<FakeCCLayerTreeHost> layerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> layerTreeHost(FakeCCLayerTreeHost::create()); layerTreeHost->setRootLayer(parent.get()); @@ -687,14 +711,14 @@ TEST(LayerChromiumLayerTreeHostTest, changeHost) child->setReplicaLayer(replica.get()); replica->setMaskLayer(mask.get()); - RefPtr<FakeCCLayerTreeHost> firstLayerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> firstLayerTreeHost(FakeCCLayerTreeHost::create()); firstLayerTreeHost->setRootLayer(parent.get()); assertLayerTreeHostMatchesForSubtree(parent.get(), firstLayerTreeHost.get()); // Now re-root the tree to a new host (simulating what we do on a context lost event). // This should update the host pointers for all layers in the tree. - RefPtr<FakeCCLayerTreeHost> secondLayerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> secondLayerTreeHost(FakeCCLayerTreeHost::create()); secondLayerTreeHost->setRootLayer(parent.get()); assertLayerTreeHostMatchesForSubtree(parent.get(), secondLayerTreeHost.get()); @@ -719,13 +743,13 @@ TEST(LayerChromiumLayerTreeHostTest, changeHostInSubtree) secondChild->addChild(secondGrandChild); firstParent->addChild(secondChild); - RefPtr<FakeCCLayerTreeHost> firstLayerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> firstLayerTreeHost(FakeCCLayerTreeHost::create()); firstLayerTreeHost->setRootLayer(firstParent.get()); assertLayerTreeHostMatchesForSubtree(firstParent.get(), firstLayerTreeHost.get()); // Now reparent the subtree starting at secondChild to a layer in a different tree. - RefPtr<FakeCCLayerTreeHost> secondLayerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> secondLayerTreeHost(FakeCCLayerTreeHost::create()); secondLayerTreeHost->setRootLayer(secondParent.get()); secondParent->addChild(secondChild); @@ -758,7 +782,7 @@ TEST(LayerChromiumLayerTreeHostTest, replaceMaskAndReplicaLayer) mask->addChild(maskChild); replica->addChild(replicaChild); - RefPtr<FakeCCLayerTreeHost> layerTreeHost = FakeCCLayerTreeHost::create(); + OwnPtr<FakeCCLayerTreeHost> layerTreeHost(FakeCCLayerTreeHost::create()); layerTreeHost->setRootLayer(parent.get()); assertLayerTreeHostMatchesForSubtree(parent.get(), layerTreeHost.get()); diff --git a/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp b/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp new file mode 100644 index 000000000..ddab17564 --- /dev/null +++ b/Source/WebKit/chromium/tests/LayerRendererChromiumTest.cpp @@ -0,0 +1,277 @@ +/* + * 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 "LayerRendererChromium.h" + +#include "FakeWebGraphicsContext3D.h" +#include "GraphicsContext3D.h" +#include "GraphicsContext3DPrivate.h" +#include "cc/CCSingleThreadProxy.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +using namespace WebCore; +using namespace WebKit; + +class FrameCountingMemoryAllocationSettingContext : public FakeWebGraphicsContext3D { +public: + FrameCountingMemoryAllocationSettingContext() : m_frame(0) { } + + // WebGraphicsContext3D methods. + + // This method would normally do a glSwapBuffers under the hood. + virtual void prepareTexture() { m_frame++; } + virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { m_memoryAllocationChangedCallback = callback; } + virtual WebString getString(WebKit::WGC3Denum name) + { + if (name == GraphicsContext3D::EXTENSIONS) + return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager GL_CHROMIUM_discard_framebuffer"); + return WebString(); + } + + // Methods added for test. + int frameCount() { return m_frame; } + void setMemoryAllocation(WebGraphicsMemoryAllocation allocation) { m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation); } + +private: + int m_frame; + WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* m_memoryAllocationChangedCallback; +}; + +class FakeLayerRendererChromiumClient : public LayerRendererChromiumClient { +public: + FakeLayerRendererChromiumClient() + : m_setFullRootLayerDamageCount(0) + , m_rootLayer(CCLayerImpl::create(1)) + { + m_rootLayer->createRenderSurface(); + } + + // LayerRendererChromiumClient methods. + virtual const IntSize& viewportSize() const OVERRIDE { static IntSize fakeSize; return fakeSize; } + virtual const CCSettings& settings() const OVERRIDE { static CCSettings fakeSettings; return fakeSettings; } + virtual void didLoseContext() OVERRIDE { } + virtual void onSwapBuffersComplete() OVERRIDE { } + virtual void setFullRootLayerDamage() OVERRIDE { m_setFullRootLayerDamageCount++; } + virtual void setContentsMemoryAllocationLimitBytes(size_t) OVERRIDE { } + + // Methods added for test. + int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCount; } + + CCLayerImpl* rootLayer() { return m_rootLayer.get(); } + +private: + int m_setFullRootLayerDamageCount; + DebugScopedSetImplThread m_implThread; + OwnPtr<CCLayerImpl> m_rootLayer; +}; + +class FakeLayerRendererChromium : public LayerRendererChromium { +public: + FakeLayerRendererChromium(LayerRendererChromiumClient* client, PassRefPtr<GraphicsContext3D> context) : LayerRendererChromium(client, context) { } + + // LayerRendererChromium methods. + + // Changing visibility to public. + using LayerRendererChromium::initialize; + using LayerRendererChromium::isFramebufferDiscarded; +}; + +class LayerRendererChromiumTest : public testing::Test { +protected: + LayerRendererChromiumTest() + : m_suggestHaveBackbufferYes(1, true) + , m_suggestHaveBackbufferNo(1, false) + , m_context(GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new FrameCountingMemoryAllocationSettingContext()), GraphicsContext3D::RenderDirectlyToHostWindow)) + , m_mockContext(*static_cast<FrameCountingMemoryAllocationSettingContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(m_context.get()))) + , m_layerRendererChromium(&m_mockClient, m_context.release()) + { + } + + virtual void SetUp() + { + m_layerRendererChromium.initialize(); + } + + void swapBuffers() + { + m_layerRendererChromium.swapBuffers(IntRect()); + } + + WebGraphicsMemoryAllocation m_suggestHaveBackbufferYes; + WebGraphicsMemoryAllocation m_suggestHaveBackbufferNo; + + RefPtr<GraphicsContext3D> m_context; + FrameCountingMemoryAllocationSettingContext& m_mockContext; + FakeLayerRendererChromiumClient m_mockClient; + FakeLayerRendererChromium m_layerRendererChromium; +}; + +// Test LayerRendererChromium discardFramebuffer functionality: +// Suggest recreating framebuffer when one already exists. +// Expected: it does nothing. +TEST_F(LayerRendererChromiumTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing) +{ + m_mockContext.setMemoryAllocation(m_suggestHaveBackbufferYes); + EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount()); + EXPECT_FALSE(m_layerRendererChromium.isFramebufferDiscarded()); + + swapBuffers(); + EXPECT_EQ(1, m_mockContext.frameCount()); +} + +// Test LayerRendererChromium discardFramebuffer functionality: +// Suggest discarding framebuffer when one exists. +// Expected: it is discarded and damage tracker is reset. +TEST_F(LayerRendererChromiumTest, SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLayer) +{ + m_mockContext.setMemoryAllocation(m_suggestHaveBackbufferNo); + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); + EXPECT_TRUE(m_layerRendererChromium.isFramebufferDiscarded()); +} + +// Test LayerRendererChromium discardFramebuffer functionality: +// Suggest discarding framebuffer when one does not exist. +// Expected: it does nothing. +TEST_F(LayerRendererChromiumTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing) +{ + m_mockContext.setMemoryAllocation(m_suggestHaveBackbufferNo); + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); + EXPECT_TRUE(m_layerRendererChromium.isFramebufferDiscarded()); + + m_mockContext.setMemoryAllocation(m_suggestHaveBackbufferNo); + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); + EXPECT_TRUE(m_layerRendererChromium.isFramebufferDiscarded()); +} + +// Test LayerRendererChromium discardFramebuffer functionality: +// Suggest discarding framebuffer, then try to swapBuffers. +// Expected: framebuffer is discarded, swaps are ignored, and damage is reset after discard and after each swap. +TEST_F(LayerRendererChromiumTest, SwapBuffersWhileBackbufferDiscardedShouldIgnoreSwapAndDamageRootLayer) +{ + m_mockContext.setMemoryAllocation(m_suggestHaveBackbufferNo); + EXPECT_TRUE(m_layerRendererChromium.isFramebufferDiscarded()); + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); + + swapBuffers(); + EXPECT_EQ(0, m_mockContext.frameCount()); + EXPECT_EQ(2, m_mockClient.setFullRootLayerDamageCount()); + + swapBuffers(); + EXPECT_EQ(0, m_mockContext.frameCount()); + EXPECT_EQ(3, m_mockClient.setFullRootLayerDamageCount()); +} + +// Test LayerRendererChromium discardFramebuffer functionality: +// Begin drawing a frame while a framebuffer is discarded. +// Expected: will recreate framebuffer. +TEST_F(LayerRendererChromiumTest, DiscardedBackbufferIsRecreatredForScopeDuration) +{ + m_mockContext.setMemoryAllocation(m_suggestHaveBackbufferNo); + EXPECT_TRUE(m_layerRendererChromium.isFramebufferDiscarded()); + EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount()); + + m_layerRendererChromium.beginDrawingFrame(m_mockClient.rootLayer()->renderSurface()); + EXPECT_FALSE(m_layerRendererChromium.isFramebufferDiscarded()); + + swapBuffers(); + EXPECT_EQ(1, m_mockContext.frameCount()); +} + +class ForbidSynchronousCallContext : public FakeWebGraphicsContext3D { +public: + ForbidSynchronousCallContext() { } + + virtual bool getActiveAttrib(WebGLId program, WGC3Duint index, ActiveInfo&) { ADD_FAILURE(); return false; } + virtual bool getActiveUniform(WebGLId program, WGC3Duint index, ActiveInfo&) { ADD_FAILURE(); return false; } + virtual void getAttachedShaders(WebGLId program, WGC3Dsizei maxCount, WGC3Dsizei* count, WebGLId* shaders) { ADD_FAILURE(); } + virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) { ADD_FAILURE(); return 0; } + virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { ADD_FAILURE(); } + virtual void getBufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } + virtual Attributes getContextAttributes() { ADD_FAILURE(); return m_attrs; } + virtual WGC3Denum getError() { ADD_FAILURE(); return 0; } + virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); } + virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, WGC3Denum attachment, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } + virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value) + { + if (pname == WebCore::GraphicsContext3D::MAX_TEXTURE_SIZE) + *value = 1024; // MAX_TEXTURE_SIZE is cached client side, so it's OK to query. + else + ADD_FAILURE(); + } + + // We allow querying the shader compilation and program link status in debug mode, but not release. + virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) + { +#ifndef NDEBUG + *value = 1; +#else + ADD_FAILURE(); +#endif + } + + virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) + { +#ifndef NDEBUG + *value = 1; +#else + ADD_FAILURE(); +#endif + } + + virtual WebString getString(WGC3Denum name) + { + // We allow querying the extension string. + // FIXME: It'd be better to check that we only do this before starting any other expensive work (like starting a compilation) + if (name != WebCore::GraphicsContext3D::EXTENSIONS) + ADD_FAILURE(); + return WebString(); + } + + virtual WebString getProgramInfoLog(WebGLId program) { ADD_FAILURE(); return WebString(); } + virtual void getRenderbufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } + + virtual WebString getShaderInfoLog(WebGLId shader) { ADD_FAILURE(); return WebString(); } + virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precisiontype, WGC3Dint* range, WGC3Dint* precision) { ADD_FAILURE(); } + virtual WebString getShaderSource(WebGLId shader) { ADD_FAILURE(); return WebString(); } + virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); } + virtual void getTexParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } + virtual void getUniformfv(WebGLId program, WGC3Dint location, WGC3Dfloat* value) { ADD_FAILURE(); } + virtual void getUniformiv(WebGLId program, WGC3Dint location, WGC3Dint* value) { ADD_FAILURE(); } + virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name) { ADD_FAILURE(); return 0; } + virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); } + virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); } + virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname) { ADD_FAILURE(); return 0; } +}; + +// This test isn't using the same fixture as LayerRendererChromiumTest, and you can't mix TEST() and TEST_F() with the same name, hence LRC2. +TEST(LayerRendererChromiumTest2, initializationDoesNotMakeSynchronousCalls) +{ + FakeLayerRendererChromiumClient mockClient; + FakeLayerRendererChromium layerRendererChromium(&mockClient, GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new ForbidSynchronousCallContext), GraphicsContext3D::RenderDirectlyToHostWindow)); + + EXPECT_TRUE(layerRendererChromium.initialize()); +} diff --git a/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp b/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp index bdbf7bb09..d1a97bb5b 100644 --- a/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp +++ b/Source/WebKit/chromium/tests/LayerTextureUpdaterTest.cpp @@ -79,6 +79,27 @@ struct PaintFillAlpha : public PaintCallback { } }; +struct PaintFillPartialOpaque : public PaintCallback { + PaintFillPartialOpaque(IntRect opaqueRect) + : m_opaqueRect(opaqueRect) + { + } + + virtual void operator()(GraphicsContext& context, const IntRect& contentRect) + { + Color alpha(0, 0, 0, 0); + context.fillRect(contentRect, alpha, ColorSpaceDeviceRGB); + + IntRect fillOpaque = m_opaqueRect; + fillOpaque.intersect(contentRect); + + Color opaque(255, 255, 255, 255); + context.fillRect(fillOpaque, opaque, ColorSpaceDeviceRGB); + } + + IntRect m_opaqueRect; +}; + #define EXPECT_EQ_RECT(a, b) \ EXPECT_EQ(a.x(), b.x()); \ EXPECT_EQ(a.maxX(), b.maxX()); \ @@ -95,19 +116,19 @@ TEST(LayerTextureUpdaterTest, testOpaqueRectPresentAfterOpaquePaint) opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 400, 400), opaqueRect); } @@ -121,19 +142,19 @@ TEST(LayerTextureUpdaterTest, testOpaqueRectNotPresentAfterNonOpaquePaint) opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); } @@ -148,21 +169,21 @@ TEST(LayerTextureUpdaterTest, testOpaqueRectNotPresentForOpaqueLayerWithOpaquePa painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillOpaque)); updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); } @@ -177,22 +198,70 @@ TEST(LayerTextureUpdaterTest, testOpaqueRectNotPresentForOpaqueLayerWithNonOpaqu painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); updater = BitmapSkPictureCanvasLayerTextureUpdater::create(painter.release(), false); updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); opaqueRect = IntRect(); painter = adoptPtr(new TestLayerPainterChromium(fillAlpha)); updater = FrameBufferSkPictureCanvasLayerTextureUpdater::create(painter.release()); updater->setOpaque(true); - updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, &opaqueRect); + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); EXPECT_EQ_RECT(IntRect(0, 0, 0, 0), opaqueRect); } +TEST(LayerTextureUpdaterTest, testPartialOpaqueRectNoTransform) +{ + IntRect partialRect(100, 200, 50, 75); + PaintFillPartialOpaque fillPartial(partialRect); + OwnPtr<TestLayerPainterChromium> painter(adoptPtr(new TestLayerPainterChromium(fillPartial))); + RefPtr<LayerTextureUpdater> updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); + + IntRect opaqueRect; + updater->prepareToUpdate(IntRect(0, 0, 400, 400), IntSize(400, 400), 0, 1, opaqueRect); + EXPECT_EQ_RECT(partialRect, opaqueRect); +} + +TEST(LayerTextureUpdaterTest, testPartialOpaqueRectTranslation) +{ + IntRect partialRect(100, 200, 50, 75); + PaintFillPartialOpaque fillPartial(partialRect); + + OwnPtr<TestLayerPainterChromium> painter(adoptPtr(new TestLayerPainterChromium(fillPartial))); + RefPtr<LayerTextureUpdater> updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); + + IntRect opaqueRect; + IntRect contentRect(11, 12, 389, 388); + updater->prepareToUpdate(contentRect, IntSize(400, 400), 0, 1, opaqueRect); + EXPECT_EQ_RECT(partialRect, opaqueRect); +} + +TEST(LayerTextureUpdaterTest, testPartialOpaqueRectScale) +{ + float contentsScale = 0.5; + + IntRect partialRect(9, 20, 50, 75); + IntRect partialDeviceRect(partialRect); + PaintFillPartialOpaque fillPartial(partialDeviceRect); + OwnPtr<TestLayerPainterChromium> painter(adoptPtr(new TestLayerPainterChromium(fillPartial))); + RefPtr<LayerTextureUpdater> updater = BitmapCanvasLayerTextureUpdater::create(painter.release(), false); + + IntRect opaqueRect; + IntRect contentRect(4, 6, 396, 394); + updater->prepareToUpdate(contentRect, IntSize(400, 400), 0, contentsScale, opaqueRect); + + // Original rect: 9, 20, 50, 75 + // Scaled down to half size: 4.5, 10, 25, 37.5 + // Enclosed int rect: 5, 10, 24, 37 + // Scaled back up to content: 10, 20, 48, 74 + IntRect scaledRect(10, 20, 48, 74); + EXPECT_EQ_RECT(scaledRect, opaqueRect); +} + } // namespace diff --git a/Source/WebKit/chromium/tests/LevelDBTest.cpp b/Source/WebKit/chromium/tests/LevelDBTest.cpp new file mode 100644 index 000000000..2a00ae686 --- /dev/null +++ b/Source/WebKit/chromium/tests/LevelDBTest.cpp @@ -0,0 +1,105 @@ +/* + * 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(LEVELDB) + +#include "FileSystem.h" +#include "LevelDBComparator.h" +#include "LevelDBDatabase.h" +#include "LevelDBSlice.h" +#include <gtest/gtest.h> +#include <webkit/support/webkit_support.h> +#include <wtf/Vector.h> + +using namespace WebCore; + +namespace { + +class SimpleComparator : public LevelDBComparator { +public: + virtual int compare(const LevelDBSlice& a, const LevelDBSlice& b) const OVERRIDE + { + size_t len = std::min(a.end() - a.begin(), b.end() - b.begin()); + return memcmp(a.begin(), b.begin(), len); + } + virtual const char* name() const OVERRIDE { return "temp_comparator"; } +}; + +Vector<char> encodeString(const std::string& s) +{ + Vector<char> ret(s.size()); + for (size_t i = 0; i < s.size(); ++i) + ret.append(s[i]); + return ret; +} + +TEST(LevelDBDatabaseTest, CorruptionTest) +{ + OwnPtr<webkit_support::ScopedTempDirectory> tempDirectory = adoptPtr(webkit_support::CreateScopedTempDirectory()); + tempDirectory->CreateUniqueTempDir(); + const String path = String::fromUTF8(tempDirectory->path().c_str()); + + const Vector<char> key = encodeString("key"); + const Vector<char> putValue = encodeString("value"); + Vector<char> gotValue; + SimpleComparator comparator; + + OwnPtr<LevelDBDatabase> leveldb = LevelDBDatabase::open(path, &comparator); + EXPECT_TRUE(leveldb); + bool success = leveldb->put(key, putValue); + EXPECT_TRUE(success); + leveldb.release(); + EXPECT_FALSE(leveldb); + + leveldb = LevelDBDatabase::open(path, &comparator); + EXPECT_TRUE(leveldb); + success = leveldb->get(key, gotValue); + EXPECT_TRUE(success); + EXPECT_EQ(putValue, gotValue); + leveldb.release(); + EXPECT_FALSE(leveldb); + + const String filepath = pathByAppendingComponent(path, "CURRENT"); + PlatformFileHandle handle = openFile(filepath, OpenForWrite); + truncateFile(handle, 0); + closeFile(handle); + + leveldb = LevelDBDatabase::open(path, &comparator); + EXPECT_FALSE(leveldb); + + bool destroyed = LevelDBDatabase::destroy(path); + EXPECT_TRUE(destroyed); + + leveldb = LevelDBDatabase::open(path, &comparator); + EXPECT_TRUE(leveldb); + success = leveldb->get(key, gotValue); + EXPECT_FALSE(success); +} + +} // namespace + +#endif // USE(LEVELDB) diff --git a/Source/WebKit/chromium/tests/LocaleWinTest.cpp b/Source/WebKit/chromium/tests/LocaleWinTest.cpp new file mode 100644 index 000000000..d64e902a5 --- /dev/null +++ b/Source/WebKit/chromium/tests/LocaleWinTest.cpp @@ -0,0 +1,163 @@ +/* + * 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER OR 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 "LocaleWin.h" + +#include <gtest/gtest.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/text/CString.h> + +using namespace WebCore; +using namespace std; + +enum { + January = 0, February, March, + April, May, June, + July, August, September, + October, November, December, +}; + +static double msForDate(int year, int month, int day) +{ + return dateToDaysFrom1970(year, month, day) * msPerDay; +} + +TEST(LocaleWinTest, TestLocalizedDateFormatText) +{ + EXPECT_STREQ("year/month/day", LocaleWin::dateFormatText("y/M/d", "year", "month", "day").utf8().data()); + EXPECT_STREQ("year/month/day", LocaleWin::dateFormatText("yy/MM/dd", "year", "month", "day").utf8().data()); + EXPECT_STREQ("year/month/day", LocaleWin::dateFormatText("yyy/MMM/ddd", "year", "month", "day").utf8().data()); + EXPECT_STREQ("year/month/day", LocaleWin::dateFormatText("yyyy/MMMM/dddd", "year", "month", "day").utf8().data()); + EXPECT_STREQ("/month/day, year", LocaleWin::dateFormatText("/MM/dd, yyyy", "year", "month", "day").utf8().data()); + EXPECT_STREQ("month/day, year=year.", LocaleWin::dateFormatText("MM/dd, 'year='yyyy.", "year", "month", "day").utf8().data()); + EXPECT_STREQ("month-day 'year", LocaleWin::dateFormatText("MM-dd ''yyy", "year", "month", "day").utf8().data()); + EXPECT_STREQ("month-day aaa'bbb year", LocaleWin::dateFormatText("MM-dd 'aaa''bbb' yyy", "year", "month", "day").utf8().data()); + EXPECT_STREQ("year/month/day/year/month/day", LocaleWin::dateFormatText("yyyy/MMMM/dddd/yyyy/MMMM/dddd", "year", "month", "day").utf8().data()); + EXPECT_STREQ("YY/mm/DD", LocaleWin::dateFormatText("YY/mm/DD", "year", "month", "day").utf8().data()); +} + +TEST(LocaleWinTest, TestFormat) +{ + const LCID EnglishUS = 0x0409; + OwnPtr<LocaleWin> locale = LocaleWin::create(EnglishUS); + + EXPECT_STREQ("4/7/2", locale->formatDate("M/d/y", 2012, 2012, April, 7).utf8().data()); + EXPECT_STREQ("4/7/2007", locale->formatDate("M/d/y", 2012, 2007, April, 7).utf8().data()); + EXPECT_STREQ("4/7/8", locale->formatDate("M/d/y", 2012, 2008, April, 7).utf8().data()); + EXPECT_STREQ("4/7/7", locale->formatDate("M/d/y", 2012, 2017, April, 7).utf8().data()); + EXPECT_STREQ("4/7/2018", locale->formatDate("M/d/y", 2012, 2018, April, 7).utf8().data()); + EXPECT_STREQ("12/31/2062", locale->formatDate("M/d/y", 2012, 2062, December, 31).utf8().data()); + EXPECT_STREQ("4/7/0002", locale->formatDate("M/d/y", 2012, 2, April, 7).utf8().data()); + + EXPECT_STREQ("04/27/12", locale->formatDate("MM/dd/yy", 2012, 2012, April, 27).utf8().data()); + EXPECT_STREQ("04/07/1962", locale->formatDate("MM/dd/yy", 2012, 1962, April, 7).utf8().data()); + EXPECT_STREQ("04/07/63", locale->formatDate("MM/dd/yy", 2012, 1963, April, 7).utf8().data()); + EXPECT_STREQ("01/31/00", locale->formatDate("MM/dd/yy", 2012, 2000, January, 31).utf8().data()); + EXPECT_STREQ("04/07/62", locale->formatDate("MM/dd/yy", 2012, 2062, April, 7).utf8().data()); + EXPECT_STREQ("04/07/2063", locale->formatDate("MM/dd/yy", 2012, 2063, April, 7).utf8().data()); + EXPECT_STREQ("04/07/0001", locale->formatDate("MM/dd/yy", 2012, 1, April, 7).utf8().data()); + + EXPECT_STREQ("Jan/7/2012", locale->formatDate("MMM/d/yyyy", 2012, 2012, January, 7).utf8().data()); + EXPECT_STREQ("Feb/7/2008", locale->formatDate("MMM/d/yyyy", 2012, 2008, February, 7).utf8().data()); + EXPECT_STREQ("Mar/7/2017", locale->formatDate("MMM/d/yyyy", 2012, 2017, March, 7).utf8().data()); + EXPECT_STREQ("Apr/7/2012", locale->formatDate("MMM/d/yyyy", 2012, 2012, April, 7).utf8().data()); + EXPECT_STREQ("May/7/0002", locale->formatDate("MMM/d/yyyy", 2012, 2, May, 7).utf8().data()); + EXPECT_STREQ("Jun/7/2008", locale->formatDate("MMM/d/yyyy", 2012, 2008, June, 7).utf8().data()); + EXPECT_STREQ("Jul/7/2017", locale->formatDate("MMM/d/yyyy", 2012, 2017, July, 7).utf8().data()); + EXPECT_STREQ("Aug/31/2062", locale->formatDate("MMM/d/yyyy", 2012, 2062, August, 31).utf8().data()); + EXPECT_STREQ("Sep/7/0002", locale->formatDate("MMM/d/yyyy", 2012, 2, September, 7).utf8().data()); + EXPECT_STREQ("Oct/7/2012", locale->formatDate("MMM/d/yyyy", 2012, 2012, October, 7).utf8().data()); + EXPECT_STREQ("Nov/7/2008", locale->formatDate("MMM/d/yyyy", 2012, 2008, November, 7).utf8().data()); + EXPECT_STREQ("Dec/31/2062", locale->formatDate("MMM/d/yyyy", 2012, 2062, December, 31).utf8().data()); + + EXPECT_STREQ("January-7-2017", locale->formatDate("MMMM-d-yyyy", 2012, 2017, January, 7).utf8().data()); + EXPECT_STREQ("February-31-2062", locale->formatDate("MMMM-d-yyyy", 2012, 2062, February, 31).utf8().data()); + EXPECT_STREQ("March-7-0002", locale->formatDate("MMMM-d-yyyy", 2012, 2, March, 7).utf8().data()); + EXPECT_STREQ("April-7-22012", locale->formatDate("MMMM-d-yyyy", 2012, 22012, April, 7).utf8().data()); + EXPECT_STREQ("May-7-12008", locale->formatDate("MMMM-d-yyyy", 2012, 12008, May, 7).utf8().data()); + EXPECT_STREQ("June-7-22012", locale->formatDate("MMMM-d-yyyy", 2012, 22012, June, 7).utf8().data()); + EXPECT_STREQ("July-7-12008", locale->formatDate("MMMM-d-yyyy", 2012, 12008, July, 7).utf8().data()); + EXPECT_STREQ("August-7-2017", locale->formatDate("MMMM-d-yyyy", 2012, 2017, August, 7).utf8().data()); + EXPECT_STREQ("September-31-2062", locale->formatDate("MMMM-d-yyyy", 2012, 2062, September, 31).utf8().data()); + EXPECT_STREQ("October-7-0002", locale->formatDate("MMMM-d-yyyy", 2012, 2, October, 7).utf8().data()); + EXPECT_STREQ("November-7-22012", locale->formatDate("MMMM-d-yyyy", 2012, 22012, November, 7).utf8().data()); + EXPECT_STREQ("December-7-12008", locale->formatDate("MMMM-d-yyyy", 2012, 12008, December, 7).utf8().data()); + + EXPECT_STREQ("Jan-1-0001", locale->formatDate("MMM-d-yyyy", 2012, 1, January, 1).utf8().data()); + EXPECT_STREQ("Sep-13-275760", locale->formatDate("MMM-d-yyyy", 2012, 275760, September, 13).utf8().data()); + + + // For the following test, we'd like to confirm they don't crash and their + // results are not important because we can assume invalid arguments are + // never passed. + EXPECT_STREQ("2012-13-00", locale->formatDate("yyyy-MM-dd", -1, 2012, December + 1, 0).utf8().data()); + EXPECT_STREQ("-1-00--1", locale->formatDate("y-MM-dd", -1, -1, -1, -1).utf8().data()); + EXPECT_STREQ("-1-124-33", locale->formatDate("y-MM-dd", 2012, -1, 123, 33).utf8().data()); + +} + +TEST(LocaleWinTest, TestParse) +{ + const LCID EnglishUS = 0x0409; + OwnPtr<LocaleWin> locale = LocaleWin::create(EnglishUS); + + EXPECT_EQ(msForDate(2012, April, 27), locale->parseDate("MM/dd/yy", 2012, "04/27/12")); + EXPECT_EQ(msForDate(2062, April, 27), locale->parseDate("MM/dd/yy", 2012, "04/27/62")); + EXPECT_EQ(msForDate(1963, April, 27), locale->parseDate("MM/dd/yy", 2012, "04/27/63")); + EXPECT_EQ(msForDate(2012, April, 27), locale->parseDate("MM/dd/yy", 2012, "4/27/2012")); + EXPECT_EQ(msForDate(2012, April, 27), locale->parseDate("MM/dd/yy", 2012, "Apr/27/2012")); + EXPECT_EQ(msForDate(2, April, 27), locale->parseDate("MM/d/yy", 2012, "April/27/2")); + EXPECT_EQ(msForDate(2, April, 27), locale->parseDate("MM/d/yy", 2012, "april/27/2")); + EXPECT_TRUE(isnan(locale->parseDate("MM/d/yy", 2012, "april/27"))); + EXPECT_TRUE(isnan(locale->parseDate("MM/d/yy", 2012, "april/27/"))); + EXPECT_TRUE(isnan(locale->parseDate("MM/d/yy", 2012, " april/27/"))); + + EXPECT_EQ(msForDate(12, April, 7), locale->parseDate("MMM/d/yyyy", 2012, "04/7/12")); + EXPECT_EQ(msForDate(62, April, 7), locale->parseDate("MMM/d/yyyy", 2012, "04/07/62")); + EXPECT_EQ(msForDate(63, April, 7), locale->parseDate("MMM/d/yyyy", 2012, "04/07/63")); + EXPECT_EQ(msForDate(2012, April, 7), locale->parseDate("MMM/d/yyyy", 2012, "4/7/2012")); + EXPECT_EQ(msForDate(2012, May, 7), locale->parseDate("MMM/d/yyyy", 2012, "May/007/2012")); + EXPECT_EQ(msForDate(2, May, 27), locale->parseDate("MM/d/yyyy", 2012, "May/0027/2")); + EXPECT_TRUE(isnan(locale->parseDate("MM/d/yyyy", 2012, "May///0027///2"))); + EXPECT_TRUE(isnan(locale->parseDate("MM/d/yyyy", 2012, "Mayyyyyy/0027/2"))); + + EXPECT_EQ(msForDate(2012, April, 27), locale->parseDate("MMMM/dd/y", 2012, "04/27/2")); + EXPECT_EQ(msForDate(2017, April, 27), locale->parseDate("MMMM/dd/y", 2012, "04/27/7")); + EXPECT_EQ(msForDate(2008, April, 27), locale->parseDate("MMMM/dd/y", 2012, "04/27/8")); + EXPECT_EQ(msForDate(2012, April, 27), locale->parseDate("MMMM/dd/y", 2012, "4/27/2012")); + EXPECT_EQ(msForDate(2012, December, 27), locale->parseDate("MMMM/dd/y", 2012, "December/27/2012")); + EXPECT_EQ(msForDate(2012, November, 27), locale->parseDate("MMMM/d/y", 2012, "November/27/2")); + EXPECT_TRUE(isnan(locale->parseDate("MMMM/d/y", 2012, "November 27 2"))); + EXPECT_TRUE(isnan(locale->parseDate("MMMM/d/y", 2012, "November 32 2"))); + EXPECT_TRUE(isnan(locale->parseDate("MMMM/d/y", 2012, "-1/-1/-1"))); +} diff --git a/Source/WebKit/chromium/tests/LocalizedNumberICUTest.cpp b/Source/WebKit/chromium/tests/LocalizedNumberICUTest.cpp index 6203d35d7..92f85c6bb 100644 --- a/Source/WebKit/chromium/tests/LocalizedNumberICUTest.cpp +++ b/Source/WebKit/chromium/tests/LocalizedNumberICUTest.cpp @@ -29,8 +29,8 @@ */ #include "config.h" -#include "LocalizedNumberICU.h" +#include "LocaleICU.h" #include <gtest/gtest.h> #include <wtf/PassOwnPtr.h> @@ -38,7 +38,7 @@ using namespace WebCore; void testNumberIsReversible(const char* localeString, const char* original, const char* shouldHave = 0) { - OwnPtr<ICULocale> locale = ICULocale::create(localeString); + OwnPtr<LocaleICU> locale = LocaleICU::create(localeString); String localized = locale->convertToLocalizedNumber(original); if (shouldHave) EXPECT_TRUE(localized.contains(shouldHave)); diff --git a/Source/WebKit/chromium/tests/MockCCQuadCuller.h b/Source/WebKit/chromium/tests/MockCCQuadCuller.h new file mode 100644 index 000000000..d9cd31326 --- /dev/null +++ b/Source/WebKit/chromium/tests/MockCCQuadCuller.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef MockCCQuadCuller_h +#define MockCCQuadCuller_h + +#include "IntRect.h" +#include "cc/CCDrawQuad.h" +#include "cc/CCQuadCuller.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class MockCCQuadCuller : public WebCore::CCQuadCuller { +public: + MockCCQuadCuller() + : CCQuadCuller(m_quadListStorage, 0, 0) + , m_activeQuadList(m_quadListStorage) + { } + + explicit MockCCQuadCuller(CCQuadList& externalQuadList) + : CCQuadCuller(externalQuadList, 0, 0) + , m_activeQuadList(externalQuadList) + { } + + virtual bool append(WTF::PassOwnPtr<WebCore::CCDrawQuad> newQuad) + { + OwnPtr<WebCore::CCDrawQuad> drawQuad = newQuad; + if (!drawQuad->quadRect().isEmpty()) { + m_activeQuadList.append(drawQuad.release()); + return true; + } + return false; + } + + const WebCore::CCQuadList& quadList() const { return m_activeQuadList; }; + +private: + WebCore::CCQuadList& m_activeQuadList; + WebCore::CCQuadList m_quadListStorage; +}; + +} // namespace WebCore +#endif // MockCCQuadCuller_h diff --git a/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp b/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp index b0d86ddf6..721f1fabb 100644 --- a/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp +++ b/Source/WebKit/chromium/tests/PlatformContextSkiaTest.cpp @@ -39,9 +39,9 @@ namespace { #define EXPECT_EQ_RECT(a, b) \ EXPECT_EQ(a.x(), b.x()); \ - EXPECT_EQ(a.maxX(), b.maxX()); \ EXPECT_EQ(a.y(), b.y()); \ - EXPECT_EQ(a.maxY(), b.maxY()); + EXPECT_EQ(a.width(), b.width()); \ + EXPECT_EQ(a.height(), b.height()); #define EXPECT_PIXELS_MATCH(bitmap, opaqueRect) \ { \ @@ -53,6 +53,17 @@ namespace { } \ } +#define EXPECT_PIXELS_MATCH_EXACT(bitmap, opaqueRect) \ +{ \ + SkAutoLockPixels locker(bitmap); \ + for (int y = 0; y < bitmap.height(); ++y) \ + for (int x = 0; x < bitmap.width(); ++x) { \ + int alpha = *bitmap.getAddr32(x, y) >> 24; \ + bool opaque = opaqueRect.contains(x, y); \ + EXPECT_EQ(opaque, alpha == 255); \ + } \ +} + TEST(PlatformContextSkiaTest, trackOpaqueTest) { SkBitmap bitmap; @@ -215,9 +226,90 @@ TEST(PlatformContextSkiaTest, trackOpaqueClipTest) context.save(); context.clipToImageBuffer(alphaImage.get(), FloatRect(30, 30, 10, 10)); context.fillRect(FloatRect(10, 10, 90, 90), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + context.restore(); EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); EXPECT_PIXELS_MATCH(bitmap, platformContext.opaqueRegion().asRect()); - context.restore(); +} + +TEST(PlatformContextSkiaTest, trackImageMask) +{ + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400); + bitmap.allocPixels(); + bitmap.eraseColor(0); + SkCanvas canvas(bitmap); + + PlatformContextSkia platformContext(&canvas); + platformContext.setTrackOpaqueRegion(true); + GraphicsContext context(&platformContext); + + Color opaque(1.0f, 0.0f, 0.0f, 1.0f); + Color alpha(0.0f, 0.0f, 0.0f, 0.0f); + + // Image masks are done by drawing a bitmap into a transparency layer that uses DstIn to mask + // out a transparency layer below that is filled with the mask color. In the end this should + // not be marked opaque. + + context.setCompositeOperation(CompositeSourceOver); + context.beginTransparencyLayer(1); + context.fillRect(FloatRect(10, 10, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + + context.setCompositeOperation(CompositeDestinationIn); + context.beginTransparencyLayer(1); + + OwnPtr<ImageBuffer> alphaImage = ImageBuffer::create(IntSize(100, 100)); + alphaImage->context()->fillRect(IntRect(0, 0, 100, 100), alpha, ColorSpaceDeviceRGB); + + context.setCompositeOperation(CompositeSourceOver); + context.drawImageBuffer(alphaImage.get(), ColorSpaceDeviceRGB, FloatRect(10, 10, 10, 10)); + + context.endTransparencyLayer(); + context.endTransparencyLayer(); + + EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); +} + +TEST(PlatformContextSkiaTest, trackImageMaskWithOpaqueRect) +{ + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400); + bitmap.allocPixels(); + bitmap.eraseColor(0); + SkCanvas canvas(bitmap); + + PlatformContextSkia platformContext(&canvas); + platformContext.setTrackOpaqueRegion(true); + GraphicsContext context(&platformContext); + + Color opaque(1.0f, 0.0f, 0.0f, 1.0f); + Color alpha(0.0f, 0.0f, 0.0f, 0.0f); + + // Image masks are done by drawing a bitmap into a transparency layer that uses DstIn to mask + // out a transparency layer below that is filled with the mask color. In the end this should + // not be marked opaque. + + context.setCompositeOperation(CompositeSourceOver); + context.beginTransparencyLayer(1); + context.fillRect(FloatRect(10, 10, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + + context.setCompositeOperation(CompositeDestinationIn); + context.beginTransparencyLayer(1); + + OwnPtr<ImageBuffer> alphaImage = ImageBuffer::create(IntSize(100, 100)); + alphaImage->context()->fillRect(IntRect(0, 0, 100, 100), alpha, ColorSpaceDeviceRGB); + + context.setCompositeOperation(CompositeSourceOver); + context.drawImageBuffer(alphaImage.get(), ColorSpaceDeviceRGB, FloatRect(10, 10, 10, 10)); + + // We can't have an opaque mask actually, but we can pretend here like it would look if we did. + context.fillRect(FloatRect(12, 12, 3, 3), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + + context.endTransparencyLayer(); + context.endTransparencyLayer(); + + EXPECT_EQ_RECT(IntRect(12, 12, 3, 3), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); } TEST(PlatformContextSkiaTest, trackOpaqueJoinTest) @@ -572,113 +664,170 @@ TEST(PlatformContextSkiaTest, trackOpaqueOvalTest) EXPECT_PIXELS_MATCH(bitmap, platformContext.opaqueRegion().asRect()); } -TEST(PlatformContextSkiaTest, layerTransformTranslateOpaqueTest) +TEST(PlatformContextSkiaTest, contextTransparencyLayerTest) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400); bitmap.allocPixels(); bitmap.eraseColor(0); SkCanvas canvas(bitmap); - AffineTransform transform; - transform.translate(10, 10); PlatformContextSkia platformContext(&canvas); platformContext.setTrackOpaqueRegion(true); - platformContext.setOpaqueRegionTransform(transform); GraphicsContext context(&platformContext); - + Color opaque(1.0f, 0.0f, 0.0f, 1.0f); - - context.fillRect(FloatRect(10, 10, 90, 90), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); - EXPECT_EQ_RECT(IntRect(20, 20, 90, 90), platformContext.opaqueRegion().asRect()); - EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect())); - - context.clearRect(FloatRect(10, 10, 90, 90)); + + context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + EXPECT_EQ_RECT(IntRect(20, 20, 10, 10), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH(bitmap, platformContext.opaqueRegion().asRect()); + + context.clearRect(FloatRect(20, 20, 10, 10)); EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); - context.translate(30, 30); + context.beginTransparencyLayer(0.5); + context.save(); + context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + context.restore(); + context.endTransparencyLayer(); + EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); - context.fillRect(FloatRect(10, 10, 90, 90), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); - EXPECT_EQ_RECT(IntRect(50, 50, 90, 90), platformContext.opaqueRegion().asRect()); - EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect())); + context.clearRect(FloatRect(20, 20, 10, 10)); + EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); - context.clearRect(FloatRect(10, 10, 90, 90)); + context.beginTransparencyLayer(0.5); + context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + context.endTransparencyLayer(); EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); } -TEST(PlatformContextSkiaTest, layerTransformScaleOpaqueTest) +TEST(PlatformContextSkiaTest, UnboundedDrawsAreClipped) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400); bitmap.allocPixels(); bitmap.eraseColor(0); SkCanvas canvas(bitmap); - AffineTransform transform; - transform.scale(2); PlatformContextSkia platformContext(&canvas); platformContext.setTrackOpaqueRegion(true); - platformContext.setOpaqueRegionTransform(transform); GraphicsContext context(&platformContext); Color opaque(1.0f, 0.0f, 0.0f, 1.0f); + Color alpha(0.0f, 0.0f, 0.0f, 0.0f); - context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); - EXPECT_EQ_RECT(IntRect(40, 40, 20, 20), platformContext.opaqueRegion().asRect()); - EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect())); + Path path; + context.setShouldAntialias(false); + context.setMiterLimit(1); + context.setStrokeThickness(5); + context.setLineCap(SquareCap); + context.setStrokeStyle(SolidStroke); - context.clearRect(FloatRect(20, 20, 10, 10)); - EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + // Make skia unable to compute fast bounds for our paths. + Vector<float> dashArray; + dashArray.append(1); + dashArray.append(0); + context.setLineDash(dashArray, 0); - context.scale(FloatSize(2, 1)); - context.translate(0, 10); + // Make the device opaque in 10,10 40x40. + context.fillRect(FloatRect(10, 10, 40, 40), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + EXPECT_EQ_RECT(IntRect(10, 10, 40, 40), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); - context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); - EXPECT_EQ_RECT(IntRect(80, 60, 40, 20), platformContext.opaqueRegion().asRect()); - EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect())); + // Clip to the left edge of the opaque area. + context.clip(IntRect(10, 10, 10, 40)); - context.clearRect(FloatRect(20, 20, 10, 10)); - EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + // Draw a path that gets clipped. This should destroy the opaque area but only inside the clip. + context.setCompositeOperation(CompositeSourceOut); + context.setFillColor(alpha, ColorSpaceDeviceRGB); + path.moveTo(FloatPoint(10, 10)); + path.addLineTo(FloatPoint(40, 40)); + context.strokePath(path); + + EXPECT_EQ_RECT(IntRect(20, 10, 30, 40), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH(bitmap, platformContext.opaqueRegion().asRect()); } -TEST(PlatformContextSkiaTest, contextTransparencyLayerTest) +TEST(PlatformContextSkiaTest, PreserveOpaqueOnlyMattersForFirstLayer) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 400, 400); bitmap.allocPixels(); bitmap.eraseColor(0); SkCanvas canvas(bitmap); - AffineTransform transform; - transform.scale(2); PlatformContextSkia platformContext(&canvas); platformContext.setTrackOpaqueRegion(true); - platformContext.setOpaqueRegionTransform(transform); GraphicsContext context(&platformContext); - + Color opaque(1.0f, 0.0f, 0.0f, 1.0f); - - context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); - EXPECT_EQ_RECT(IntRect(40, 40, 20, 20), platformContext.opaqueRegion().asRect()); - EXPECT_PIXELS_MATCH(bitmap, transform.inverse().mapRect(platformContext.opaqueRegion().asRect())); - - context.clearRect(FloatRect(20, 20, 10, 10)); - EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + Color alpha(0.0f, 0.0f, 0.0f, 0.0f); + Path path; + context.setShouldAntialias(false); + context.setMiterLimit(1); + context.setStrokeThickness(5); + context.setLineCap(SquareCap); + context.setStrokeStyle(SolidStroke); + + // Make skia unable to compute fast bounds for our paths. + Vector<float> dashArray; + dashArray.append(1); + dashArray.append(0); + context.setLineDash(dashArray, 0); + + // Make the device opaque in 10,10 40x40. + context.fillRect(FloatRect(10, 10, 40, 40), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + EXPECT_EQ_RECT(IntRect(10, 10, 40, 40), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); + + // Begin a layer that preserves opaque. + context.setCompositeOperation(CompositeSourceOver); context.beginTransparencyLayer(0.5); - context.save(); - context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); - context.restore(); + + // Begin a layer that does not preserve opaque. + context.setCompositeOperation(CompositeSourceOut); + context.beginTransparencyLayer(0.5); + + // This should not destroy the device opaqueness. + context.fillRect(FloatRect(10, 10, 40, 40), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + + // This should not destroy the device opaqueness either. + context.setFillColor(opaque, ColorSpaceDeviceRGB); + path.moveTo(FloatPoint(10, 10)); + path.addLineTo(FloatPoint(40, 40)); + context.strokePath(path); + context.endTransparencyLayer(); - EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + context.endTransparencyLayer(); + EXPECT_EQ_RECT(IntRect(10, 10, 40, 40), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); - context.clearRect(FloatRect(20, 20, 10, 10)); + // Now begin a layer that does not preserve opaque and draw through it to the device. + context.setCompositeOperation(CompositeSourceOut); + context.beginTransparencyLayer(0.5); + + // This should destroy the device opaqueness. + context.fillRect(FloatRect(10, 10, 40, 40), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + + context.endTransparencyLayer(); EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); + + // Now we draw with a path for which it cannot compute fast bounds. This should destroy the entire opaque region. + context.setCompositeOperation(CompositeSourceOut); context.beginTransparencyLayer(0.5); - context.fillRect(FloatRect(20, 20, 10, 10), opaque, ColorSpaceDeviceRGB, CompositeSourceOver); + + // This should nuke the device opaqueness. + context.setFillColor(opaque, ColorSpaceDeviceRGB); + path.moveTo(FloatPoint(10, 10)); + path.addLineTo(FloatPoint(40, 40)); + context.strokePath(path); + context.endTransparencyLayer(); EXPECT_EQ_RECT(IntRect(), platformContext.opaqueRegion().asRect()); + EXPECT_PIXELS_MATCH_EXACT(bitmap, platformContext.opaqueRegion().asRect()); } } // namespace diff --git a/Source/WebKit/chromium/tests/PlatformGestureCurveTest.cpp b/Source/WebKit/chromium/tests/PlatformGestureCurveTest.cpp index 74ca57667..362727c04 100644 --- a/Source/WebKit/chromium/tests/PlatformGestureCurveTest.cpp +++ b/Source/WebKit/chromium/tests/PlatformGestureCurveTest.cpp @@ -29,7 +29,7 @@ #include "ActivePlatformGestureAnimation.h" #include "PlatformGestureCurveTarget.h" -#include "TouchFlingPlatformGestureCurve.h" +#include "TouchpadFlingPlatformGestureCurve.h" #include "WheelFlingPlatformGestureCurve.h" #include "cc/CCActiveGestureAnimation.h" #include "cc/CCGestureCurve.h" @@ -42,7 +42,7 @@ using namespace WebCore; class MockPlatformGestureCurveTarget : public PlatformGestureCurveTarget { public: - virtual void setScrollIncrement(const IntPoint& delta) + virtual void scrollBy(const IntPoint& delta) { m_cumulativeDelta = m_cumulativeDelta + delta; } @@ -57,7 +57,7 @@ private: TEST(PlatformGestureCurve, flingCurve) { MockPlatformGestureCurveTarget target; - OwnPtr<ActivePlatformGestureAnimation> animation = ActivePlatformGestureAnimation::create(0, WheelFlingPlatformGestureCurve::create(FloatPoint(100, 0)), &target); + OwnPtr<ActivePlatformGestureAnimation> animation = ActivePlatformGestureAnimation::create(WheelFlingPlatformGestureCurve::create(FloatPoint(100, 0)), &target); // Note: the expectations below are dependent on the value of sigma hard-coded in the Rayleigh // curve. If sigma changes, these test expectations will also change. @@ -69,23 +69,24 @@ TEST(PlatformGestureCurve, flingCurve) EXPECT_FALSE(animation->animate(1001)); // Since the Rayleigh CDF maxes out at 1, we expect the cumulative scroll increments to // match the input velocity parameter. - // Since we can be off by +/-0.5 on each conversion to int for setScrollIncrement, - // pick the 'nearness' to be within the number of times animate returns true. EXPECT_NEAR(target.cumulativeDelta().x(), 100, 1); EXPECT_EQ(target.cumulativeDelta().y(), 0); + // Test animation when not starting at t = 0. double baseTime = 42.42; - animation = ActivePlatformGestureAnimation::create(baseTime, WheelFlingPlatformGestureCurve::create(FloatPoint(100, 0)), &target); + animation = ActivePlatformGestureAnimation::create(WheelFlingPlatformGestureCurve::create(FloatPoint(100, 0)), &target); target.resetCumulativeDelta(); EXPECT_TRUE(animation->animate(baseTime + 0.35)); EXPECT_TRUE(animation->animate(baseTime + 1.35)); - EXPECT_FALSE(animation->animate(baseTime + 1000)); + EXPECT_TRUE(animation->animate(baseTime + 1000)); + EXPECT_FALSE(animation->animate(baseTime + 1001)); EXPECT_NEAR(target.cumulativeDelta().x(), 100, 1); - animation = ActivePlatformGestureAnimation::create(0, WheelFlingPlatformGestureCurve::create(FloatPoint(50, 150)), &target); + animation = ActivePlatformGestureAnimation::create(WheelFlingPlatformGestureCurve::create(FloatPoint(50, 150)), &target); target.resetCumulativeDelta(); + // Test animation with both horizontal and vertical scroll velocities. EXPECT_TRUE(animation->animate(0)); EXPECT_TRUE(animation->animate(0.25)); EXPECT_TRUE(animation->animate(0.45)); @@ -98,21 +99,18 @@ TEST(PlatformGestureCurve, flingCurve) TEST(PlatformGestureCurve, flingCurveTouch) { + double initialVelocity = 5000; MockPlatformGestureCurveTarget target; - OwnPtr<ActivePlatformGestureAnimation> animation = ActivePlatformGestureAnimation::create(0, TouchFlingPlatformGestureCurve::create(FloatPoint(1000, 0)), &target); + OwnPtr<ActivePlatformGestureAnimation> animation = ActivePlatformGestureAnimation::create(TouchpadFlingPlatformGestureCurve::create(FloatPoint(initialVelocity, 0)), &target); - // Note: the expectations below are dependent on the value of sigma hard-coded in the Rayleigh - // curve. If sigma changes, these test expectations will also change. + // Note: the expectations below are dependent on the value of sigma hard-coded in the curve parameters. + // If the parameters change, then the tests values/expectations will need to be updated. EXPECT_TRUE(animation->animate(0)); EXPECT_TRUE(animation->animate(0.25)); EXPECT_TRUE(animation->animate(0.45)); // Use non-uniform tick spacing. - EXPECT_TRUE(animation->animate(0.75)); - EXPECT_TRUE(animation->animate(0.9)); - EXPECT_TRUE(animation->animate(1000)); - EXPECT_FALSE(animation->animate(1001)); - // Since we can be off by +/-0.5 on each conversion to int for setScrollIncrement, - // pick the 'nearness' to be within the 5 * number of times animate returns true. - EXPECT_NEAR(target.cumulativeDelta().x(), 1000, 1); + EXPECT_TRUE(animation->animate(1)); + EXPECT_FALSE(animation->animate(1.5)); + EXPECT_NEAR(target.cumulativeDelta().x(), 1193, 1); EXPECT_EQ(target.cumulativeDelta().y(), 0); } diff --git a/Source/WebKit/chromium/tests/PopupMenuTest.cpp b/Source/WebKit/chromium/tests/PopupMenuTest.cpp index f053ca410..1db65ec6e 100644 --- a/Source/WebKit/chromium/tests/PopupMenuTest.cpp +++ b/Source/WebKit/chromium/tests/PopupMenuTest.cpp @@ -42,6 +42,7 @@ #include "PopupMenu.h" #include "PopupMenuClient.h" #include "PopupMenuChromium.h" +#include "RuntimeEnabledFeatures.h" #include "WebDocument.h" #include "WebElement.h" #include "WebFrame.h" @@ -154,7 +155,7 @@ public: // We need to override this so that the popup menu size is not 0 // (the layout code checks to see if the popup fits on the screen). virtual WebScreenInfo screenInfo() - { + { WebScreenInfo screenInfo; screenInfo.availableRect.height = 2000; screenInfo.availableRect.width = 2000; @@ -181,6 +182,10 @@ public: protected: virtual void SetUp() { + // When touch is enabled, padding is added to option elements + // In these tests, we'll assume touch is disabled. + m_touchWasEnabled = RuntimeEnabledFeatures::touchEnabled(); + RuntimeEnabledFeatures::setTouchEnabled(false); m_webView = static_cast<WebViewImpl*>(WebView::create(&m_webviewClient)); m_webView->initializeMainFrame(&m_webFrameClient); m_popupMenu = adoptRef(new PopupMenuChromium(&m_popupMenuClient)); @@ -191,6 +196,7 @@ protected: m_popupMenu = 0; m_webView->close(); webkit_support::UnregisterAllMockedURLs(); + RuntimeEnabledFeatures::setTouchEnabled(m_touchWasEnabled); } // Returns true if there currently is a select popup in the WebView. @@ -278,6 +284,7 @@ protected: TestWebFrameClient m_webFrameClient; TestPopupMenuClient m_popupMenuClient; RefPtr<PopupMenu> m_popupMenu; + bool m_touchWasEnabled; std::string baseURL; }; diff --git a/Source/WebKit/chromium/tests/RegionTest.cpp b/Source/WebKit/chromium/tests/RegionTest.cpp new file mode 100644 index 000000000..d202ab40c --- /dev/null +++ b/Source/WebKit/chromium/tests/RegionTest.cpp @@ -0,0 +1,127 @@ +/* + * 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 "Region.h" + +#include <gtest/gtest.h> + +using namespace WebCore; + +namespace { + +#define TEST_INSIDE_RECT(r, x, y, w, h) \ + EXPECT_TRUE(r.contains(IntPoint(x, y))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y))); \ + EXPECT_TRUE(r.contains(IntPoint(x, y + h - 1))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y + h - 1))); \ + EXPECT_TRUE(r.contains(IntPoint(x, y + h / 2))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w - 1, y + h / 2))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y + h - 1))); \ + EXPECT_TRUE(r.contains(IntPoint(x + w / 2, y + h / 2))); \ + +#define TEST_LEFT_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x - 1, y))); \ + EXPECT_FALSE(r.contains(IntPoint(x - 1, y + h - 1))); \ + +#define TEST_RIGHT_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x + w, y))); \ + EXPECT_FALSE(r.contains(IntPoint(x + w, y + h - 1))); \ + +#define TEST_TOP_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x, y - 1))); \ + EXPECT_FALSE(r.contains(IntPoint(x + w - 1, y - 1))); \ + +#define TEST_BOTTOM_OF_RECT(r, x, y, w, h) \ + EXPECT_FALSE(r.contains(IntPoint(x, y + h))); \ + EXPECT_FALSE(r.contains(IntPoint(x + w - 1, y + h))); \ + +TEST(RegionTest, containsPoint) +{ + Region r; + + EXPECT_FALSE(r.contains(IntPoint(0, 0))); + + r.unite(IntRect(35, 35, 1, 1)); + TEST_INSIDE_RECT(r, 35, 35, 1, 1); + TEST_LEFT_OF_RECT(r, 35, 35, 1, 1); + TEST_RIGHT_OF_RECT(r, 35, 35, 1, 1); + TEST_TOP_OF_RECT(r, 35, 35, 1, 1); + TEST_BOTTOM_OF_RECT(r, 35, 35, 1, 1); + + r.unite(IntRect(30, 30, 10, 10)); + TEST_INSIDE_RECT(r, 30, 30, 10, 10); + TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); + TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); + TEST_TOP_OF_RECT(r, 30, 30, 10, 10); + TEST_BOTTOM_OF_RECT(r, 30, 30, 10, 10); + + r.unite(IntRect(31, 40, 10, 10)); + EXPECT_FALSE(r.contains(IntPoint(30, 40))); + EXPECT_TRUE(r.contains(IntPoint(31, 40))); + EXPECT_FALSE(r.contains(IntPoint(40, 39))); + EXPECT_TRUE(r.contains(IntPoint(40, 40))); + + TEST_INSIDE_RECT(r, 30, 30, 10, 10); + TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); + TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); + TEST_TOP_OF_RECT(r, 30, 30, 10, 10); + TEST_INSIDE_RECT(r, 31, 40, 10, 10); + TEST_LEFT_OF_RECT(r, 31, 40, 10, 10); + TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10); + TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10); + + r.unite(IntRect(42, 40, 10, 10)); + + TEST_INSIDE_RECT(r, 42, 40, 10, 10); + TEST_LEFT_OF_RECT(r, 42, 40, 10, 10); + TEST_RIGHT_OF_RECT(r, 42, 40, 10, 10); + TEST_TOP_OF_RECT(r, 42, 40, 10, 10); + TEST_BOTTOM_OF_RECT(r, 42, 40, 10, 10); + + TEST_INSIDE_RECT(r, 30, 30, 10, 10); + TEST_LEFT_OF_RECT(r, 30, 30, 10, 10); + TEST_RIGHT_OF_RECT(r, 30, 30, 10, 10); + TEST_TOP_OF_RECT(r, 30, 30, 10, 10); + TEST_INSIDE_RECT(r, 31, 40, 10, 10); + TEST_LEFT_OF_RECT(r, 31, 40, 10, 10); + TEST_RIGHT_OF_RECT(r, 31, 40, 10, 10); + TEST_BOTTOM_OF_RECT(r, 31, 40, 10, 10); +} + +TEST(RegionTest, emptySpan) +{ + Region r; + r.unite(IntRect(5, 0, 10, 10)); + r.unite(IntRect(0, 5, 10, 10)); + r.subtract(IntRect(7, 7, 10, 0)); + + Vector<IntRect> rects = r.rects(); + for (size_t i = 0; i < rects.size(); ++i) + EXPECT_FALSE(rects[i].isEmpty()); +} + +} // namespace diff --git a/Source/WebKit/chromium/tests/RenderTableCellTest.cpp b/Source/WebKit/chromium/tests/RenderTableCellTest.cpp index 049b53534..b4c554a76 100644 --- a/Source/WebKit/chromium/tests/RenderTableCellTest.cpp +++ b/Source/WebKit/chromium/tests/RenderTableCellTest.cpp @@ -91,25 +91,12 @@ TEST_F(RenderTableCellDeathTest, CanSetColumn) EXPECT_EQ(columnIndex, m_cell->col()); } -TEST_F(RenderTableCellDeathTest, CanSetRow) -{ - static const unsigned rowIndex = 10; - m_cell->setRow(rowIndex); - EXPECT_EQ(rowIndex, m_cell->row()); -} - TEST_F(RenderTableCellDeathTest, CanSetColumnToMaxColumnIndex) { m_cell->setCol(maxColumnIndex); EXPECT_EQ(maxColumnIndex, m_cell->col()); } -TEST_F(RenderTableCellDeathTest, CanSetRowToMaxRowIndex) -{ - m_cell->setRow(maxRowIndex); - EXPECT_EQ(maxRowIndex, m_cell->row()); -} - // FIXME: Re-enable these tests once ASSERT_DEATH is supported for Android. // See: https://bugs.webkit.org/show_bug.cgi?id=74089 #if !OS(ANDROID) @@ -119,21 +106,11 @@ TEST_F(RenderTableCellDeathTest, CrashIfColumnOverflowOnSetting) ASSERT_DEATH(m_cell->setCol(maxColumnIndex + 1), ""); } -TEST_F(RenderTableCellDeathTest, CrashIfRowOverflowOnSetting) -{ - ASSERT_DEATH(m_cell->setRow(maxRowIndex + 1), ""); -} - TEST_F(RenderTableCellDeathTest, CrashIfSettingUnsetColumnIndex) { ASSERT_DEATH(m_cell->setCol(unsetColumnIndex), ""); } -TEST_F(RenderTableCellDeathTest, CrashIfSettingUnsetRowIndex) -{ - ASSERT_DEATH(m_cell->setRow(unsetRowIndex), ""); -} - #endif } diff --git a/Source/WebKit/chromium/tests/RenderTableRowTest.cpp b/Source/WebKit/chromium/tests/RenderTableRowTest.cpp new file mode 100644 index 000000000..a5ee2a347 --- /dev/null +++ b/Source/WebKit/chromium/tests/RenderTableRowTest.cpp @@ -0,0 +1,118 @@ +/* + * 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. + */ +// FIXME: If we get the TestWebKitAPI framework to bring a full Frame + DOM stack +// in a portable way, this test should be shared with all ports! + +#include "config.h" + +#include "RenderTableRow.h" + +#include "Document.h" +#include "Frame.h" +#include "FrameTestHelpers.h" +#include "RenderArena.h" +#include "WebFrame.h" +#include "WebFrameImpl.h" +#include "WebView.h" + +#include <gtest/gtest.h> + +using namespace WebKit; + +namespace WebCore { + +namespace { + +class RenderTableRowDeathTest : public testing::Test { + // It's unfortunate that we have to get the whole browser stack to test one RenderObject + // but the code needs it. + static Frame* frame() + { + static WebView* webView; + + if (webView) + return static_cast<WebFrameImpl*>(webView->mainFrame())->frame(); + + webView = FrameTestHelpers::createWebViewAndLoad("about:blank"); + webView->setFocus(true); + return static_cast<WebFrameImpl*>(webView->mainFrame())->frame(); + } + + static Document* document() + { + return frame()->document(); + } + + static RenderArena* arena() + { + return document()->renderArena(); + } + + virtual void SetUp() + { + m_row = new (arena()) RenderTableRow(document()); + } + + virtual void TearDown() + { + m_row->destroy(); + } + +protected: + RenderTableRow* m_row; +}; + +TEST_F(RenderTableRowDeathTest, CanSetRow) +{ + static const unsigned rowIndex = 10; + m_row->setRowIndex(rowIndex); + EXPECT_EQ(rowIndex, m_row->rowIndex()); +} + +TEST_F(RenderTableRowDeathTest, CanSetRowToMaxRowIndex) +{ + m_row->setRowIndex(maxRowIndex); + EXPECT_EQ(maxRowIndex, m_row->rowIndex()); +} + +// FIXME: Re-enable these tests once ASSERT_DEATH is supported for Android. +// See: https://bugs.webkit.org/show_bug.cgi?id=74089 +#if !OS(ANDROID) + +TEST_F(RenderTableRowDeathTest, CrashIfRowOverflowOnSetting) +{ + ASSERT_DEATH(m_row->setRowIndex(maxRowIndex + 1), ""); +} + +TEST_F(RenderTableRowDeathTest, CrashIfSettingUnsetRowIndex) +{ + ASSERT_DEATH(m_row->setRowIndex(unsetRowIndex), ""); +} + +#endif + +} + +} // namespace WebCore diff --git a/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp b/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp index 9d21b8a9e..0827267ed 100644 --- a/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp +++ b/Source/WebKit/chromium/tests/ScrollAnimatorNoneTest.cpp @@ -125,7 +125,7 @@ TEST(ScrollAnimatorEnabled, Enabled) scrollAnimatorNone.reset(); scrollAnimatorNone.scroll(HorizontalScrollbar, ScrollByPixel, 4, 25); - EXPECT_NE(100, scrollAnimatorNone.currentX()); + EXPECT_EQ(100, scrollAnimatorNone.currentX()); EXPECT_NE(0, scrollAnimatorNone.currentX()); EXPECT_EQ(0, scrollAnimatorNone.currentY()); scrollAnimatorNone.reset(); @@ -692,21 +692,21 @@ TEST_F(ScrollAnimatorNoneTest, ScrollWheelTrace) ScrollAnimatorNone::Parameters parameters(true, 11 * kTickTime, 0, ScrollAnimatorNone::Cubic, 3 * kTickTime, ScrollAnimatorNone::Cubic, 3 * kTickTime, ScrollAnimatorNone::Linear, 0); // Constructed from an actual scroll wheel trace that exhibited a glitch. - bool result = updateDataFromParameters(1, 53.33, 1000, 100.5781, ¶meters); + bool result = updateDataFromParameters(1, 53.33f, 1000, 100.5781f, ¶meters); result = animateScroll(100.5933); result = result && animateScroll(100.6085); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.6485, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.6485f, ¶meters); result = result && animateScroll(100.6515); result = result && animateScroll(100.6853); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.6863, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.6863f, ¶meters); result = result && animateScroll(100.7005); result = result && animateScroll(100.7157); result = result && animateScroll(100.7312); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.7379, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.7379f, ¶meters); result = result && animateScroll(100.7464); result = result && animateScroll(100.7617); result = result && animateScroll(100.7775); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.7779, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.7779f, ¶meters); for (double t = 100.7928; result && t < 200; t += 0.015) result = result && animateScroll(t); } @@ -716,21 +716,21 @@ TEST_F(ScrollAnimatorNoneTest, ScrollWheelTraceSmoothed) ScrollAnimatorNone::Parameters parameters(true, 11 * kTickTime, 7 * kTickTime, ScrollAnimatorNone::Cubic, 3 * kTickTime, ScrollAnimatorNone::Cubic, 3 * kTickTime, ScrollAnimatorNone::Linear, 0); // Constructed from an actual scroll wheel trace that exhibited a glitch. - bool result = updateDataFromParameters(1, 53.33, 1000, 100.5781, ¶meters); + bool result = updateDataFromParameters(1, 53.33f, 1000, 100.5781f, ¶meters); result = animateScroll(100.5933); result = result && animateScroll(100.6085); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.6485, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.6485f, ¶meters); result = result && animateScroll(100.6515); result = result && animateScroll(100.6853); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.6863, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.6863f, ¶meters); result = result && animateScroll(100.7005); result = result && animateScroll(100.7157); result = result && animateScroll(100.7312); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.7379, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.7379f, ¶meters); result = result && animateScroll(100.7464); result = result && animateScroll(100.7617); result = result && animateScroll(100.7775); - result = result && updateDataFromParameters(1, 53.33, 1000, 100.7779, ¶meters); + result = result && updateDataFromParameters(1, 53.33f, 1000, 100.7779f, ¶meters); for (double t = 100.7928; result && t < 200; t += 0.015) result = result && animateScroll(t); } diff --git a/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp index 78b96b495..5bf990cde 100644 --- a/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/ScrollbarLayerChromiumTest.cpp @@ -27,6 +27,7 @@ #include "ScrollbarLayerChromium.h" #include "Scrollbar.h" +#include "Settings.h" #include "TreeSynchronizer.h" #include "cc/CCScrollbarLayerImpl.h" #include "cc/CCSingleThreadProxy.h" @@ -90,6 +91,7 @@ TEST(ScrollbarLayerChromiumTest, resolveScrollLayerPointer) { DebugScopedSetImplThread impl; + Settings::setMockScrollbarsEnabled(true); { RefPtr<MockScrollbar> scrollbar = adoptRef(new MockScrollbar); RefPtr<LayerChromium> layerTreeRoot = LayerChromium::create(); diff --git a/Source/WebKit/chromium/tests/TextureCopierTest.cpp b/Source/WebKit/chromium/tests/TextureCopierTest.cpp new file mode 100644 index 000000000..17173c0a5 --- /dev/null +++ b/Source/WebKit/chromium/tests/TextureCopierTest.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2011 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 "TextureCopier.h" + +#include "FakeWebGraphicsContext3D.h" +#include "GraphicsContext3DPrivate.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <wtf/RefPtr.h> + +using namespace WebCore; +using namespace WebKit; +using testing::InSequence; +using testing::Test; +using testing::_; + +namespace { + +class MockContext : public FakeWebGraphicsContext3D { +public: + MOCK_METHOD2(bindFramebuffer, void(WGC3Denum, WebGLId)); + MOCK_METHOD3(texParameteri, void(GC3Denum target, GC3Denum pname, GC3Dint param)); + + MOCK_METHOD3(drawArrays, void(GC3Denum mode, GC3Dint first, GC3Dsizei count)); +}; + +TEST(TextureCopierTest, testDrawArraysCopy) +{ + GraphicsContext3D::Attributes attrs; + RefPtr<GraphicsContext3D> context = GraphicsContext3DPrivate::createGraphicsContextFromWebContext(adoptPtr(new MockContext()), GraphicsContext3D::RenderDirectlyToHostWindow); + MockContext& mockContext = *static_cast<MockContext*>(GraphicsContext3DPrivate::extractWebGraphicsContext3D(context.get())); + + { + InSequence sequence; + + // Here we check just some essential properties of copyTexture() to avoid mirroring the full implementation. + EXPECT_CALL(mockContext, bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, _)); + + // Make sure linear filtering is disabled during the copy. + EXPECT_CALL(mockContext, texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST)); + EXPECT_CALL(mockContext, texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::NEAREST)); + + EXPECT_CALL(mockContext, drawArrays(_, _, _)); + + // Linear filtering should be restored. + EXPECT_CALL(mockContext, texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR)); + EXPECT_CALL(mockContext, texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR)); + + // Default framebuffer should be restored + EXPECT_CALL(mockContext, bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0)); + } + + int sourceTextureId = 1; + int destTextureId = 2; + IntSize size(256, 128); + OwnPtr<AcceleratedTextureCopier> copier(AcceleratedTextureCopier::create(context)); + copier->copyTexture(context.get(), sourceTextureId, destTextureId, size); +} + +} // namespace diff --git a/Source/WebKit/chromium/tests/TextureManagerTest.cpp b/Source/WebKit/chromium/tests/TextureManagerTest.cpp index 3b576af99..d5313f5dc 100644 --- a/Source/WebKit/chromium/tests/TextureManagerTest.cpp +++ b/Source/WebKit/chromium/tests/TextureManagerTest.cpp @@ -24,23 +24,18 @@ #include "config.h" +#include "CCTiledLayerTestCommon.h" #include "ManagedTexture.h" #include "TextureManager.h" #include <gtest/gtest.h> using namespace WebCore; +using namespace WebKitTests; using namespace WTF; namespace { -class FakeTextureAllocator : public TextureAllocator { -public: - virtual ~FakeTextureAllocator() { } - virtual unsigned createTexture(const IntSize&, GC3Denum) { return 1; } - virtual void deleteTexture(unsigned, const IntSize&, GC3Denum) { } -}; - class TextureManagerTest : public testing::Test { public: TextureManagerTest() diff --git a/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp b/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp index 2523230e0..5b7cb0df7 100644 --- a/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp +++ b/Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp @@ -26,18 +26,16 @@ #include "TiledLayerChromium.h" +#include "CCAnimationTestCommon.h" #include "CCLayerTreeTestCommon.h" +#include "CCTiledLayerTestCommon.h" #include "FakeCCLayerTreeHostClient.h" -#include "LayerTextureUpdater.h" -#include "Region.h" -#include "TextureManager.h" #include "WebCompositor.h" #include "cc/CCSingleThreadProxy.h" // For DebugScopedSetImplThread -#include "cc/CCTextureUpdater.h" -#include "cc/CCTiledLayerImpl.h" #include <gtest/gtest.h> using namespace WebCore; +using namespace WebKitTests; using namespace WTF; #define EXPECT_EQ_RECT(a, b) \ @@ -48,208 +46,38 @@ using namespace WTF; namespace { -class FakeTextureAllocator : public TextureAllocator { +class TestCCOcclusionTracker : public CCOcclusionTracker { public: - virtual unsigned createTexture(const IntSize&, GC3Denum) { return 0; } - virtual void deleteTexture(unsigned, const IntSize&, GC3Denum) { } -}; - -class FakeTiledLayerChromium; - -class FakeLayerTextureUpdater : public LayerTextureUpdater { -public: - class Texture : public LayerTextureUpdater::Texture { - public: - Texture(FakeLayerTextureUpdater* layer, PassOwnPtr<ManagedTexture> texture) - : LayerTextureUpdater::Texture(texture) - , m_layer(layer) - { - } - virtual ~Texture() { } - - virtual void updateRect(GraphicsContext3D*, TextureAllocator*, const IntRect&, const IntRect&) { m_layer->updateRect(); } - virtual void prepareRect(const IntRect&) { m_layer->prepareRect(); } - - private: - FakeLayerTextureUpdater* m_layer; - }; - - FakeLayerTextureUpdater() - : m_prepareCount(0) - , m_updateCount(0) - , m_prepareRectCount(0) - { - } - virtual ~FakeLayerTextureUpdater() { } - - // Sets the rect to invalidate during the next call to prepareToUpdate(). After the next - // call to prepareToUpdate() the rect is reset. - void setRectToInvalidate(const IntRect&, FakeTiledLayerChromium*); - - // Number of times prepareToUpdate has been invoked. - int prepareCount() const { return m_prepareCount; } - void clearPrepareCount() { m_prepareCount = 0; } - - // Number of times updateRect has been invoked. - int updateCount() const { return m_updateCount; } - void clearUpdateCount() { m_updateCount = 0; } - void updateRect() { m_updateCount++; } - - // Number of times prepareRect() has been invoked on a texture. - int prepareRectCount() const { return m_prepareRectCount; } - void clearPrepareRectCount() { m_prepareRectCount = 0; } - void prepareRect() { m_prepareRectCount++; } - - void setOpaquePaintRect(const IntRect& opaquePaintRect) { m_opaquePaintRect = opaquePaintRect; } - - // Last rect passed to prepareToUpdate(). - const IntRect& lastUpdateRect() const { return m_lastUpdateRect; } - - virtual PassOwnPtr<LayerTextureUpdater::Texture> createTexture(TextureManager* manager) { return adoptPtr(new Texture(this, ManagedTexture::create(manager))); } - virtual SampledTexelFormat sampledTexelFormat(GC3Denum) { return SampledTexelFormatRGBA; } - virtual void prepareToUpdate(const IntRect& contentRect, const IntSize&, int, float, IntRect* resultingOpaqueRect); - -private: - int m_prepareCount; - int m_updateCount; - int m_prepareRectCount; - IntRect m_rectToInvalidate; - IntRect m_lastUpdateRect; - IntRect m_opaquePaintRect; - RefPtr<FakeTiledLayerChromium> m_layer; -}; - -class FakeCCTiledLayerImpl : public CCTiledLayerImpl { -public: - explicit FakeCCTiledLayerImpl(int id) - : CCTiledLayerImpl(id) { } - virtual ~FakeCCTiledLayerImpl() { } - - bool hasTileAt(int i, int j) + TestCCOcclusionTracker() + : CCOcclusionTracker(IntRect(0, 0, 1000, 1000), true) + , m_scissorRectInScreen(IntRect(0, 0, 1000, 1000)) { - return CCTiledLayerImpl::hasTileAt(i, j); + // Pretend we have visited a render surface. + m_stack.append(StackObject()); } -}; -class FakeTiledLayerChromium : public TiledLayerChromium { -public: - explicit FakeTiledLayerChromium(TextureManager* textureManager) - : TiledLayerChromium() - , m_fakeTextureUpdater(adoptRef(new FakeLayerTextureUpdater)) - , m_textureManager(textureManager) - { - setTileSize(IntSize(100, 100)); - setTextureFormat(GraphicsContext3D::RGBA); - setBorderTexelOption(CCLayerTilingData::NoBorderTexels); - setIsDrawable(true); // So that we don't get false positives if any of these tests expect to return false from drawsContent() for other reasons. - } - virtual ~FakeTiledLayerChromium() { } + void setOcclusion(const Region& occlusion) { m_stack.last().occlusionInScreen = occlusion; } - void invalidateRect(const IntRect& rect) - { - TiledLayerChromium::invalidateRect(rect); - } - - void prepareToUpdate(const IntRect& rect, const Region& occluded) - { - TiledLayerChromium::prepareToUpdate(rect, occluded); - } - - void prepareToUpdateIdle(const IntRect& rect, const Region& occluded) - { - TiledLayerChromium::prepareToUpdateIdle(rect, occluded); - } - - bool needsIdlePaint(const IntRect& rect) - { - return TiledLayerChromium::needsIdlePaint(rect); - } - - bool skipsDraw() const - { - return TiledLayerChromium::skipsDraw(); - } - - virtual void setNeedsDisplayRect(const FloatRect& rect) - { - m_lastNeedsDisplayRect = rect; - TiledLayerChromium::setNeedsDisplayRect(rect); - } - - const FloatRect& lastNeedsDisplayRect() const { return m_lastNeedsDisplayRect; } - - FakeLayerTextureUpdater* fakeLayerTextureUpdater() { return m_fakeTextureUpdater.get(); } - - virtual TextureManager* textureManager() const { return m_textureManager; } - - virtual void paintContentsIfDirty(const Region& occludedScreenSpace) - { - prepareToUpdate(visibleLayerRect(), occludedScreenSpace); - } +protected: + virtual IntRect layerScissorRectInTargetSurface(const LayerChromium* layer) const { return m_scissorRectInScreen; } private: - virtual LayerTextureUpdater* textureUpdater() const - { - return m_fakeTextureUpdater.get(); - } - - virtual void createTextureUpdaterIfNeeded() { } - - RefPtr<FakeLayerTextureUpdater> m_fakeTextureUpdater; - TextureManager* m_textureManager; - FloatRect m_lastNeedsDisplayRect; + IntRect m_scissorRectInScreen; }; -class FakeTiledLayerWithScaledBounds : public FakeTiledLayerChromium { -public: - explicit FakeTiledLayerWithScaledBounds(TextureManager* textureManager) - : FakeTiledLayerChromium(textureManager) - { - } - - void setContentBounds(const IntSize& contentBounds) { m_forcedContentBounds = contentBounds; } - virtual IntSize contentBounds() const { return m_forcedContentBounds; } - - FloatRect updateRect() { return m_updateRect; } - -protected: - IntSize m_forcedContentBounds; -}; - -void FakeLayerTextureUpdater::setRectToInvalidate(const IntRect& rect, FakeTiledLayerChromium* layer) -{ - m_rectToInvalidate = rect; - m_layer = layer; -} - -void FakeLayerTextureUpdater::prepareToUpdate(const IntRect& contentRect, const IntSize&, int, float, IntRect* resultingOpaqueRect) -{ - m_prepareCount++; - m_lastUpdateRect = contentRect; - if (!m_rectToInvalidate.isEmpty()) { - m_layer->invalidateRect(m_rectToInvalidate); - m_rectToInvalidate = IntRect(); - m_layer = 0; - } - *resultingOpaqueRect = m_opaquePaintRect; -} - TEST(TiledLayerChromiumTest, pushDirtyTiles) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - Region noOcclusion; - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; // The tile size is 100x100, so this invalidates and then paints two tiles. layer->setBounds(IntSize(100, 200)); layer->invalidateRect(IntRect(0, 0, 100, 200)); - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); layer->pushPropertiesTo(layerImpl.get()); // We should have both tiles on the impl side. @@ -261,8 +89,7 @@ TEST(TiledLayerChromiumTest, pushDirtyTiles) // Invalidates both tiles... layer->invalidateRect(IntRect(0, 0, 100, 200)); // ....but then only update one of them. - layer->prepareToUpdate(IntRect(0, 0, 100, 100), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 100), 0); layer->pushPropertiesTo(layerImpl.get()); // We should only have the first tile since the other tile was invalidated but not painted. @@ -276,18 +103,22 @@ TEST(TiledLayerChromiumTest, pushOccludedDirtyTiles) RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - Region noOcclusion; + TestCCOcclusionTracker occluded; - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; // The tile size is 100x100, so this invalidates and then paints two tiles. layer->setBounds(IntSize(100, 200)); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); + layer->setVisibleLayerRect(IntRect(0, 0, 100, 200)); layer->invalidateRect(IntRect(0, 0, 100, 200)); - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), &occluded); layer->pushPropertiesTo(layerImpl.get()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + // We should have both tiles on the impl side. EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); EXPECT_TRUE(layerImpl->hasTileAt(0, 1)); @@ -297,16 +128,59 @@ TEST(TiledLayerChromiumTest, pushOccludedDirtyTiles) // Invalidates part of the top tile... layer->invalidateRect(IntRect(0, 0, 50, 50)); // ....but the area is occluded. - Region occlusion(IntRect(0, 0, 50, 50)); - layer->prepareToUpdate(IntRect(0, 0, 100, 100), occlusion); - layer->updateCompositorResources(0, updater); + occluded.setOcclusion(IntRect(0, 0, 50, 50)); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 100), &occluded); layer->pushPropertiesTo(layerImpl.get()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 2500, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + // We should still have both tiles, as part of the top tile is still unoccluded. EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); EXPECT_TRUE(layerImpl->hasTileAt(0, 1)); } +TEST(TiledLayerChromiumTest, pushDeletedTiles) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + + CCTextureUpdater updater; + FakeTextureAllocator allocator; + + // The tile size is 100x100, so this invalidates and then paints two tiles. + layer->setBounds(IntSize(100, 200)); + layer->invalidateRect(IntRect(0, 0, 100, 200)); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); + layer->pushPropertiesTo(layerImpl.get()); + + // We should have both tiles on the impl side. + EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); + EXPECT_TRUE(layerImpl->hasTileAt(0, 1)); + + textureManager->evictAndDeleteAllTextures(&allocator); + textureManager->setMaxMemoryLimitBytes(4*1024*1024); + textureManager->setPreferredMemoryLimitBytes(4*1024*1024); + + // This should drop the tiles on the impl thread. + layer->pushPropertiesTo(layerImpl.get()); + + // We should now have no textures on the impl thread. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); + EXPECT_FALSE(layerImpl->hasTileAt(0, 1)); + + // This should recreate and update the deleted textures. + layer->updateLayerRect(updater, IntRect(0, 0, 100, 100), 0); + layer->pushPropertiesTo(layerImpl.get()); + + // We should only have the first tile since the other tile was invalidated but not painted. + EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); + EXPECT_FALSE(layerImpl->hasTileAt(0, 1)); +} + TEST(TiledLayerChromiumTest, pushIdlePaintTiles) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); @@ -314,25 +188,22 @@ TEST(TiledLayerChromiumTest, pushIdlePaintTiles) DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; // The tile size is 100x100. Setup 5x5 tiles with one visible tile in the center. IntSize contentBounds(500, 500); IntRect contentRect(IntPoint::zero(), contentBounds); IntRect visibleRect(200, 200, 100, 100); - Region noOcclusion; // This invalidates 25 tiles and then paints one visible tile. layer->setBounds(contentBounds); layer->setVisibleLayerRect(visibleRect); layer->invalidateRect(contentRect); - layer->prepareToUpdate(visibleRect, noOcclusion); + layer->updateLayerRect(updater, visibleRect, 0); // We should need idle-painting for 3x3 tiles in the center. EXPECT_TRUE(layer->needsIdlePaint(visibleRect)); - layer->updateCompositorResources(0, updater); layer->pushPropertiesTo(layerImpl.get()); // We should have one tile on the impl side. @@ -342,10 +213,9 @@ TEST(TiledLayerChromiumTest, pushIdlePaintTiles) // For the next four updates, we should detect we still need idle painting. for (int i = 0; i < 4; i++) { - layer->prepareToUpdate(visibleRect, noOcclusion); + layer->updateLayerRect(updater, visibleRect, 0); EXPECT_TRUE(layer->needsIdlePaint(visibleRect)); - layer->prepareToUpdateIdle(visibleRect, noOcclusion); - layer->updateCompositorResources(0, updater); + layer->idleUpdateLayerRect(updater, visibleRect, 0); layer->pushPropertiesTo(layerImpl.get()); textureManager->unprotectAllTextures(); } @@ -365,6 +235,109 @@ TEST(TiledLayerChromiumTest, pushIdlePaintTiles) } } +TEST(TiledLayerChromiumTest, pushTilesAfterIdlePaintFailed) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(1024*1024, 1024*1024, 1024); + DebugScopedSetImplThread implThread; + RefPtr<FakeTiledLayerChromium> layer1 = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + OwnPtr<FakeCCTiledLayerImpl> layerImpl1(adoptPtr(new FakeCCTiledLayerImpl(0))); + RefPtr<FakeTiledLayerChromium> layer2 = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + OwnPtr<FakeCCTiledLayerImpl> layerImpl2(adoptPtr(new FakeCCTiledLayerImpl(0))); + + CCTextureUpdater updater; + FakeTextureAllocator allocator; + FakeTextureCopier copier; + FakeTextureUploader uploader; + + // For this test we have two layers. layer1 exhausts most texture memory, leaving room for 2 more tiles from + // layer2, but not all three tiles. First we paint layer1, and one tile from layer2. Then when we idle paint + // layer2, we will fail on the third tile of layer2, and this should not leave the second tile in a bad state. + + // This requires 4*30000 bytes of memory. + IntRect layer2Rect(0, 0, 100, 300); + layer2->setBounds(layer2Rect.size()); + layer2->setVisibleLayerRect(layer2Rect); + layer2->invalidateRect(layer2Rect); + + // This uses 960000 bytes, leaving 88576 bytes of memory left, which is enough for 2 tiles only in the other layer. + IntRect layerRect(IntPoint::zero(), IntSize(100, 2400)); + layer1->setBounds(layerRect.size()); + layer1->setVisibleLayerRect(layerRect); + layer1->invalidateRect(layerRect); + layer1->updateLayerRect(updater, layerRect, 0); + + // Paint a single tile in layer2 so that it will idle paint. + layer2->updateLayerRect(updater, IntRect(0, 0, 100, 100), 0); + + // We should need idle-painting for both remaining tiles in layer2. + EXPECT_TRUE(layer2->needsIdlePaint(layer2Rect)); + + // Commit the frame over to impl. + updater.update(0, &allocator, &copier, &uploader, 5000); + layer1->pushPropertiesTo(layerImpl1.get()); + layer2->pushPropertiesTo(layerImpl2.get()); + + // Now idle paint layer2. We are going to run out of memory though! + layer2->updateLayerRect(updater, IntRect(0, 0, 100, 100), 0); + layer2->idleUpdateLayerRect(updater, layer2Rect, 0); + + // Oh well, commit the frame and push. + updater.update(0, &allocator, &copier, &uploader, 5000); + layer1->pushPropertiesTo(layerImpl1.get()); + layer2->pushPropertiesTo(layerImpl2.get()); + + // Sanity check, we should have textures for the big layer. + EXPECT_TRUE(layerImpl1->hasTextureIdForTileAt(0, 0)); + + // We should only have the first tile from layer2 since it failed to idle update. + EXPECT_TRUE(layerImpl2->hasTileAt(0, 0)); + EXPECT_TRUE(layerImpl2->hasTextureIdForTileAt(0, 0)); + EXPECT_FALSE(layerImpl2->hasTileAt(0, 1)); + EXPECT_FALSE(layerImpl2->hasTileAt(0, 2)); + + // Now if layer2 becomes fully visible, we should be able to paint it and push valid textures. + textureManager->unprotectAllTextures(); + + layer2->updateLayerRect(updater, layer2Rect, 0); + layer1->updateLayerRect(updater, IntRect(), 0); + + updater.update(0, &allocator, &copier, &uploader, 5000); + layer1->pushPropertiesTo(layerImpl1.get()); + layer2->pushPropertiesTo(layerImpl2.get()); + + EXPECT_TRUE(layerImpl2->hasTileAt(0, 0)); + EXPECT_TRUE(layerImpl2->hasTileAt(0, 1)); + EXPECT_TRUE(layerImpl2->hasTileAt(0, 2)); + EXPECT_TRUE(layerImpl2->hasTextureIdForTileAt(0, 0)); + EXPECT_TRUE(layerImpl2->hasTextureIdForTileAt(0, 1)); + EXPECT_TRUE(layerImpl2->hasTextureIdForTileAt(0, 2)); +} + +TEST(TiledLayerChromiumTest, pushIdlePaintedOccludedTiles) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + TestCCOcclusionTracker occluded; + + CCTextureUpdater updater; + + // The tile size is 100x100, so this invalidates one occluded tile, culls it during paint, but prepaints it. + occluded.setOcclusion(IntRect(0, 0, 100, 100)); + + layer->setBounds(IntSize(100, 100)); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); + layer->setVisibleLayerRect(IntRect(0, 0, 100, 100)); + layer->invalidateRect(IntRect(0, 0, 100, 100)); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 100), &occluded); + layer->idleUpdateLayerRect(updater, IntRect(0, 0, 100, 100), &occluded); + layer->pushPropertiesTo(layerImpl.get()); + + // We should have the prepainted tile on the impl side. + EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); +} + TEST(TiledLayerChromiumTest, pushTilesMarkedDirtyDuringPaint) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); @@ -372,9 +345,7 @@ TEST(TiledLayerChromiumTest, pushTilesMarkedDirtyDuringPaint) DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); - Region noOcclusion; + CCTextureUpdater updater; // The tile size is 100x100, so this invalidates and then paints two tiles. // However, during the paint, we invalidate one of the tiles. This should @@ -382,8 +353,7 @@ TEST(TiledLayerChromiumTest, pushTilesMarkedDirtyDuringPaint) layer->setBounds(IntSize(100, 200)); layer->invalidateRect(IntRect(0, 0, 100, 200)); layer->fakeLayerTextureUpdater()->setRectToInvalidate(IntRect(0, 50, 100, 50), layer.get()); - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); layer->pushPropertiesTo(layerImpl.get()); // We should have both tiles on the impl side. @@ -400,23 +370,18 @@ TEST(TiledLayerChromiumTest, pushTilesLayerMarkedDirtyDuringPaintOnNextLayer) OwnPtr<FakeCCTiledLayerImpl> layer1Impl(adoptPtr(new FakeCCTiledLayerImpl(0))); OwnPtr<FakeCCTiledLayerImpl> layer2Impl(adoptPtr(new FakeCCTiledLayerImpl(0))); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); - Region noOcclusion; + CCTextureUpdater updater; layer1->setBounds(IntSize(100, 200)); layer1->invalidateRect(IntRect(0, 0, 100, 200)); layer2->setBounds(IntSize(100, 200)); layer2->invalidateRect(IntRect(0, 0, 100, 200)); - layer1->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); + layer1->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); // Invalidate a tile on layer1 layer2->fakeLayerTextureUpdater()->setRectToInvalidate(IntRect(0, 50, 100, 50), layer1.get()); - layer2->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); - - layer1->updateCompositorResources(0, updater); - layer2->updateCompositorResources(0, updater); + layer2->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); layer1->pushPropertiesTo(layer1Impl.get()); layer2->pushPropertiesTo(layer2Impl.get()); @@ -437,9 +402,7 @@ TEST(TiledLayerChromiumTest, pushTilesLayerMarkedDirtyDuringPaintOnPreviousLayer OwnPtr<FakeCCTiledLayerImpl> layer1Impl(adoptPtr(new FakeCCTiledLayerImpl(0))); OwnPtr<FakeCCTiledLayerImpl> layer2Impl(adoptPtr(new FakeCCTiledLayerImpl(0))); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); - Region noOcclusion; + CCTextureUpdater updater; layer1->setBounds(IntSize(100, 200)); layer1->invalidateRect(IntRect(0, 0, 100, 200)); @@ -448,12 +411,9 @@ TEST(TiledLayerChromiumTest, pushTilesLayerMarkedDirtyDuringPaintOnPreviousLayer // Invalidate a tile on layer2 layer1->fakeLayerTextureUpdater()->setRectToInvalidate(IntRect(0, 50, 100, 50), layer2.get()); - layer1->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); + layer1->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); - layer2->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); - - layer1->updateCompositorResources(0, updater); - layer2->updateCompositorResources(0, updater); + layer2->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); layer1->pushPropertiesTo(layer1Impl.get()); layer2->pushPropertiesTo(layer2Impl.get()); @@ -471,7 +431,6 @@ TEST(TiledLayerChromiumTest, idlePaintOutOfMemory) IntSize contentBounds(300, 300); IntRect contentRect(IntPoint::zero(), contentBounds); IntRect visibleRect(100, 100, 100, 100); - Region noOcclusion; // We have enough memory for only the visible rect, so we will run out of memory in first idle paint. int memoryLimit = 4 * 100 * 100; // 2 tiles, 4 bytes per pixel. @@ -481,51 +440,243 @@ TEST(TiledLayerChromiumTest, idlePaintOutOfMemory) DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; // This invalidates 9 tiles and then paints one visible tile. layer->setBounds(contentBounds); layer->setVisibleLayerRect(visibleRect); layer->invalidateRect(contentRect); - layer->prepareToUpdate(visibleRect, noOcclusion); + layer->updateLayerRect(updater, visibleRect, 0); // We should need idle-painting for 3x3 tiles surounding visible tile. EXPECT_TRUE(layer->needsIdlePaint(visibleRect)); - layer->updateCompositorResources(0, updater); layer->pushPropertiesTo(layerImpl.get()); // We should have one tile on the impl side. EXPECT_TRUE(layerImpl->hasTileAt(1, 1)); textureManager->unprotectAllTextures(); - layer->prepareToUpdate(visibleRect, noOcclusion); - layer->prepareToUpdateIdle(visibleRect, noOcclusion); + layer->updateLayerRect(updater, visibleRect, 0); + layer->idleUpdateLayerRect(updater, visibleRect, 0); // We shouldn't signal we need another idle paint after we run out of memory. EXPECT_FALSE(layer->needsIdlePaint(visibleRect)); - layer->updateCompositorResources(0, updater); layer->pushPropertiesTo(layerImpl.get()); } +TEST(TiledLayerChromiumTest, idlePaintZeroSizedLayer) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(20000, 10000, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + + CCTextureUpdater updater; + + // The layer's bounds are empty. + IntRect contentRect; + + layer->setBounds(contentRect.size()); + layer->setVisibleLayerRect(contentRect); + layer->invalidateRect(contentRect); + layer->updateLayerRect(updater, contentRect, 0); + + // Empty layers don't have tiles. + EXPECT_EQ(0u, layer->numPaintedTiles()); + + // Empty layers don't need prepaint. + EXPECT_FALSE(layer->needsIdlePaint(contentRect)); + + layer->pushPropertiesTo(layerImpl.get()); + + // Empty layers don't have tiles. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); + + // Non-visible layers don't idle paint. + layer->idleUpdateLayerRect(updater, contentRect, 0); + + // Empty layers don't have tiles. + EXPECT_EQ(0u, layer->numPaintedTiles()); + + layer->pushPropertiesTo(layerImpl.get()); + + // Empty layers don't have tiles. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); +} + +TEST(TiledLayerChromiumTest, idlePaintZeroSizedAnimatingLayer) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(20000, 10000, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + + CCTextureUpdater updater; + + // Pretend the layer is animating. + layer->setDrawTransformIsAnimating(true); + + // The layer's bounds are empty. + IntRect contentRect; + + layer->setBounds(contentRect.size()); + layer->setVisibleLayerRect(contentRect); + layer->invalidateRect(contentRect); + layer->updateLayerRect(updater, contentRect, 0); + + // Empty layers don't have tiles. + EXPECT_EQ(0u, layer->numPaintedTiles()); + + // Empty layers don't need prepaint. + EXPECT_FALSE(layer->needsIdlePaint(contentRect)); + + layer->pushPropertiesTo(layerImpl.get()); + + // Empty layers don't have tiles. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); + + // Non-visible layers don't idle paint. + layer->idleUpdateLayerRect(updater, contentRect, 0); + + // Empty layers don't have tiles. + EXPECT_EQ(0u, layer->numPaintedTiles()); + + layer->pushPropertiesTo(layerImpl.get()); + + // Empty layers don't have tiles. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); +} + +TEST(TiledLayerChromiumTest, idlePaintNonVisibleLayers) +{ + IntSize contentBounds(100, 100); + IntRect contentRect(IntPoint::zero(), contentBounds); + + OwnPtr<TextureManager> textureManager = TextureManager::create(20000, 10000, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + + CCTextureUpdater updater; + + // Invalidate the layer but make none of it visible, so nothing paints. + IntRect visibleRect; + + layer->setBounds(contentBounds); + layer->setVisibleLayerRect(visibleRect); + layer->invalidateRect(contentRect); + layer->updateLayerRect(updater, visibleRect, 0); + + // Non-visible layers don't need idle paint. + EXPECT_FALSE(layer->needsIdlePaint(visibleRect)); + + layer->pushPropertiesTo(layerImpl.get()); + + // We should not have any tiles pushed since the layer is not visible. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); + + // Non-visible layers don't idle paint. + layer->idleUpdateLayerRect(updater, visibleRect, 0); + + layer->pushPropertiesTo(layerImpl.get()); + + // We should not have any tiles pushed since the layer is not visible. + EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); +} + +static void idlePaintRepeat(int repeatTimes, FakeTiledLayerChromium* layer, FakeCCTiledLayerImpl* layerImpl, CCTextureUpdater& updater, const IntRect& visibleRect) +{ + for (int i = 0; i < repeatTimes; ++i) { + layer->updateLayerRect(updater, visibleRect, 0); + layer->idleUpdateLayerRect(updater, visibleRect, 0); + layer->pushPropertiesTo(layerImpl); + } +} + +static void testHaveOuterTiles(FakeCCTiledLayerImpl* layerImpl, int width, int height, int have) +{ + for (int i = 0; i < width; ++i) { + for (int j = 0; j < height; ++j) { + bool hasTile = i < have || j < have || i >= width - have || j >= height - have; + EXPECT_EQ(hasTile, layerImpl->hasTileAt(i, j)); + } + } +} + +TEST(TiledLayerChromiumTest, idlePaintNonVisibleAnimatingLayers) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(8000*8000*8, 8000*8000*4, 1024); + DebugScopedSetImplThread implThread; + + CCTextureUpdater updater; + + int tileWidth = FakeTiledLayerChromium::tileSize().width(); + int tileHeight = FakeTiledLayerChromium::tileSize().height(); + int width[] = { 1, 2, 3, 4, 9, 10, 0 }; + int height[] = { 1, 2, 3, 4, 9, 10, 0 }; + + for (int j = 0; height[j]; ++j) { + for (int i = 0; width[i]; ++i) { + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + + // Pretend the layer is animating. + layer->setDrawTransformIsAnimating(true); + + IntSize contentBounds(width[i] * tileWidth, height[j] * tileHeight); + IntRect contentRect(IntPoint::zero(), contentBounds); + IntRect visibleRect; + + layer->setBounds(contentBounds); + layer->setVisibleLayerRect(visibleRect); + layer->invalidateRect(contentRect); + + // If idlePaintRect gives back a non-empty result then we should paint it. Otherwise, + // we shoud paint nothing. + bool shouldPrepaint = !layer->idlePaintRect(visibleRect).isEmpty(); + + // This paints the layer but there's nothing visible so it's a no-op. + layer->updateLayerRect(updater, visibleRect, 0); + layer->pushPropertiesTo(layerImpl.get()); + + // We should not have any tiles pushed yet since the layer is not visible and we've not prepainted. + testHaveOuterTiles(layerImpl.get(), width[i], height[j], 0); + + // Normally we don't allow non-visible layers to pre-paint, but if they are animating then we should. + EXPECT_EQ(shouldPrepaint, layer->needsIdlePaint(visibleRect)); + + // If the layer is to be prepainted at all, then after four updates we should have the outer row/columns painted. + idlePaintRepeat(4, layer.get(), layerImpl.get(), updater, visibleRect); + testHaveOuterTiles(layerImpl.get(), width[i], height[j], shouldPrepaint ? 1 : 0); + + // We don't currently idle paint past the outermost tiles. + EXPECT_FALSE(layer->needsIdlePaint(visibleRect)); + idlePaintRepeat(4, layer.get(), layerImpl.get(), updater, visibleRect); + testHaveOuterTiles(layerImpl.get(), width[i], height[j], shouldPrepaint ? 1 : 0); + } + } +} + TEST(TiledLayerChromiumTest, invalidateFromPrepare) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - Region noOcclusion; - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; + FakeTextureAllocator fakeAllocator; + FakeTextureCopier fakeCopier; + FakeTextureUploader fakeUploader; + RefPtr<GraphicsContext3D> context = createCompositorMockGraphicsContext3D(GraphicsContext3D::Attributes()); // The tile size is 100x100, so this invalidates and then paints two tiles. layer->setBounds(IntSize(100, 200)); layer->invalidateRect(IntRect(0, 0, 100, 200)); - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); + updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); layer->pushPropertiesTo(layerImpl.get()); // We should have both tiles on the impl side. @@ -535,20 +686,23 @@ TEST(TiledLayerChromiumTest, invalidateFromPrepare) textureManager->unprotectAllTextures(); layer->fakeLayerTextureUpdater()->clearPrepareCount(); - // Invoke prepareToUpdate again. As the layer is valid prepareToUpdate shouldn't be invoked on + // Invoke updateLayerRect again. As the layer is valid updateLayerRect shouldn't be invoked on // the LayerTextureUpdater. - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); + updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); EXPECT_EQ(0, layer->fakeLayerTextureUpdater()->prepareCount()); layer->invalidateRect(IntRect(0, 0, 50, 50)); - // setRectToInvalidate triggers invalidateRect() being invoked from prepareToUpdate. + // setRectToInvalidate triggers invalidateRect() being invoked from updateLayerRect. layer->fakeLayerTextureUpdater()->setRectToInvalidate(IntRect(25, 25, 50, 50), layer.get()); layer->fakeLayerTextureUpdater()->clearPrepareCount(); - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); + updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); EXPECT_EQ(1, layer->fakeLayerTextureUpdater()->prepareCount()); layer->fakeLayerTextureUpdater()->clearPrepareCount(); - // The layer should still be invalid as prepareToUpdate invoked invalidate. - layer->prepareToUpdate(IntRect(0, 0, 100, 200), noOcclusion); + // The layer should still be invalid as updateLayerRect invoked invalidate. + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); + updater.update(context.get(), &fakeAllocator, &fakeCopier, &fakeUploader, 1000); EXPECT_EQ(1, layer->fakeLayerTextureUpdater()->prepareCount()); } @@ -559,10 +713,8 @@ TEST(TiledLayerChromiumTest, verifyUpdateRectWhenContentBoundsAreScaled) OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerWithScaledBounds> layer = adoptRef(new FakeTiledLayerWithScaledBounds(textureManager.get())); - Region noOcclusion; - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; IntRect layerBounds(0, 0, 300, 200); IntRect contentBounds(0, 0, 200, 250); @@ -574,21 +726,18 @@ TEST(TiledLayerChromiumTest, verifyUpdateRectWhenContentBoundsAreScaled) // On first update, the updateRect includes all tiles, even beyond the boundaries of the layer. // However, it should still be in layer space, not content space. layer->invalidateRect(contentBounds); - layer->prepareToUpdate(contentBounds, noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, contentBounds, 0); EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 300, 300 * 0.8), layer->updateRect()); // After the tiles are updated once, another invalidate only needs to update the bounds of the layer. layer->invalidateRect(contentBounds); - layer->prepareToUpdate(contentBounds, noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, contentBounds, 0); EXPECT_FLOAT_RECT_EQ(FloatRect(layerBounds), layer->updateRect()); // Partial re-paint should also be represented by the updateRect in layer space, not content space. IntRect partialDamage(30, 100, 10, 10); layer->invalidateRect(partialDamage); - layer->prepareToUpdate(contentBounds, noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, contentBounds, 0); EXPECT_FLOAT_RECT_EQ(FloatRect(45, 80, 15, 8), layer->updateRect()); } @@ -599,9 +748,7 @@ TEST(TiledLayerChromiumTest, verifyInvalidationWhenContentsScaleChanges) DebugScopedSetImplThread implThread; OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); - Region noOcclusion; + CCTextureUpdater updater; // Create a layer with one tile. layer->setBounds(IntSize(100, 100)); @@ -611,8 +758,7 @@ TEST(TiledLayerChromiumTest, verifyInvalidationWhenContentsScaleChanges) EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 100, 100), layer->lastNeedsDisplayRect()); // Push the tiles to the impl side and check that there is exactly one. - layer->prepareToUpdate(IntRect(0, 0, 100, 100), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 100), 0); layer->pushPropertiesTo(layerImpl.get()); EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); EXPECT_FALSE(layerImpl->hasTileAt(0, 1)); @@ -625,8 +771,7 @@ TEST(TiledLayerChromiumTest, verifyInvalidationWhenContentsScaleChanges) EXPECT_FLOAT_RECT_EQ(FloatRect(0, 0, 100, 100), layer->lastNeedsDisplayRect()); // The impl side should get 2x2 tiles now. - layer->prepareToUpdate(IntRect(0, 0, 200, 200), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(0, 0, 200, 200), 0); layer->pushPropertiesTo(layerImpl.get()); EXPECT_TRUE(layerImpl->hasTileAt(0, 0)); EXPECT_TRUE(layerImpl->hasTileAt(0, 1)); @@ -636,8 +781,7 @@ TEST(TiledLayerChromiumTest, verifyInvalidationWhenContentsScaleChanges) // Invalidate the entire layer again, but do not paint. All tiles should be gone now from the // impl side. layer->setNeedsDisplay(); - layer->prepareToUpdate(IntRect(0, 0, 0, 0), noOcclusion); - layer->updateCompositorResources(0, updater); + layer->updateLayerRect(updater, IntRect(1, 0, 0, 1), 0); layer->pushPropertiesTo(layerImpl.get()); EXPECT_FALSE(layerImpl->hasTileAt(0, 0)); EXPECT_FALSE(layerImpl->hasTileAt(0, 1)); @@ -650,7 +794,7 @@ TEST(TiledLayerChromiumTest, skipsDrawGetsReset) // Initialize without threading support. WebKit::WebCompositor::initialize(0); FakeCCLayerTreeHostClient fakeCCLayerTreeHostClient; - RefPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, CCSettings()); + OwnPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, CCSettings()); // Create two 300 x 300 tiled layers. IntSize contentBounds(300, 300); @@ -671,14 +815,12 @@ TEST(TiledLayerChromiumTest, skipsDrawGetsReset) rootLayer->invalidateRect(contentRect); childLayer->invalidateRect(contentRect); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; ccLayerTreeHost->setRootLayer(rootLayer); ccLayerTreeHost->setViewportSize(IntSize(300, 300)); textureManager->setMaxMemoryLimitBytes(memoryLimit); - ccLayerTreeHost->updateLayers(); - ccLayerTreeHost->updateCompositorResources(ccLayerTreeHost->context(), updater); + ccLayerTreeHost->updateLayers(updater); // We'll skip the root layer. EXPECT_TRUE(rootLayer->skipsDraw()); @@ -690,7 +832,7 @@ TEST(TiledLayerChromiumTest, skipsDrawGetsReset) // Remove the child layer. rootLayer->removeAllChildren(); - ccLayerTreeHost->updateLayers(); + ccLayerTreeHost->updateLayers(updater); EXPECT_FALSE(rootLayer->skipsDraw()); ccLayerTreeHost->setRootLayer(0); @@ -702,16 +844,30 @@ TEST(TiledLayerChromiumTest, resizeToSmaller) { OwnPtr<TextureManager> textureManager = TextureManager::create(60*1024*1024, 60*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region noOcclusion; + CCTextureUpdater updater; layer->setBounds(IntSize(700, 700)); layer->invalidateRect(IntRect(0, 0, 700, 700)); - layer->prepareToUpdate(IntRect(0, 0, 700, 700), noOcclusion); + layer->updateLayerRect(updater, IntRect(0, 0, 700, 700), 0); layer->setBounds(IntSize(200, 200)); layer->invalidateRect(IntRect(0, 0, 200, 200)); } +TEST(TiledLayerChromiumTest, hugeLayerUpdateCrash) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(60*1024*1024, 60*1024*1024, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + CCTextureUpdater updater; + + int size = 1 << 30; + layer->setBounds(IntSize(size, size)); + layer->invalidateRect(IntRect(0, 0, size, size)); + + // Ensure no crash for bounds where size * size would overflow an int. + layer->updateLayerRect(updater, IntRect(0, 0, 700, 700), 0); +} + TEST(TiledLayerChromiumTest, partialUpdates) { CCSettings settings; @@ -719,7 +875,7 @@ TEST(TiledLayerChromiumTest, partialUpdates) // Initialize without threading support. WebKit::WebCompositor::initialize(0); FakeCCLayerTreeHostClient fakeCCLayerTreeHostClient; - RefPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, settings); + OwnPtr<CCLayerTreeHost> ccLayerTreeHost = CCLayerTreeHost::create(&fakeCCLayerTreeHostClient, settings); // Create one 500 x 300 tiled layer. IntSize contentBounds(300, 200); @@ -731,51 +887,92 @@ TEST(TiledLayerChromiumTest, partialUpdates) layer->setPosition(FloatPoint(150, 150)); layer->invalidateRect(contentRect); - FakeTextureAllocator textureAllocator; - CCTextureUpdater updater(&textureAllocator); + CCTextureUpdater updater; + FakeTextureAllocator allocator; + FakeTextureCopier copier; + FakeTextureUploader uploader; ccLayerTreeHost->setRootLayer(layer); ccLayerTreeHost->setViewportSize(IntSize(300, 200)); // Full update of all 6 tiles. - ccLayerTreeHost->updateLayers(); - ccLayerTreeHost->updateCompositorResources(ccLayerTreeHost->context(), updater); - updater.update(0, 4); - EXPECT_EQ(4, layer->fakeLayerTextureUpdater()->updateCount()); - EXPECT_TRUE(updater.hasMoreUpdates()); - layer->fakeLayerTextureUpdater()->clearUpdateCount(); - updater.update(0, 4); - EXPECT_EQ(2, layer->fakeLayerTextureUpdater()->updateCount()); - EXPECT_FALSE(updater.hasMoreUpdates()); - layer->fakeLayerTextureUpdater()->clearUpdateCount(); + ccLayerTreeHost->updateLayers(updater); + { + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(4, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_TRUE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(2, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_FALSE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + layer->pushPropertiesTo(layerImpl.get()); + } ccLayerTreeHost->commitComplete(); // Full update of 3 tiles and partial update of 3 tiles. layer->invalidateRect(IntRect(0, 0, 300, 150)); - ccLayerTreeHost->updateLayers(); - ccLayerTreeHost->updateCompositorResources(ccLayerTreeHost->context(), updater); - updater.update(0, 4); - EXPECT_EQ(3, layer->fakeLayerTextureUpdater()->updateCount()); - EXPECT_TRUE(updater.hasMoreUpdates()); - layer->fakeLayerTextureUpdater()->clearUpdateCount(); - updater.update(0, 4); - EXPECT_EQ(3, layer->fakeLayerTextureUpdater()->updateCount()); - EXPECT_FALSE(updater.hasMoreUpdates()); - layer->fakeLayerTextureUpdater()->clearUpdateCount(); + ccLayerTreeHost->updateLayers(updater); + { + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(3, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_TRUE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(3, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_FALSE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + layer->pushPropertiesTo(layerImpl.get()); + } ccLayerTreeHost->commitComplete(); // Partial update of 6 tiles. layer->invalidateRect(IntRect(50, 50, 200, 100)); - ccLayerTreeHost->updateLayers(); - ccLayerTreeHost->updateCompositorResources(ccLayerTreeHost->context(), updater); - updater.update(0, 4); - EXPECT_EQ(2, layer->fakeLayerTextureUpdater()->updateCount()); - EXPECT_TRUE(updater.hasMoreUpdates()); - layer->fakeLayerTextureUpdater()->clearUpdateCount(); - updater.update(0, 4); - EXPECT_EQ(4, layer->fakeLayerTextureUpdater()->updateCount()); - EXPECT_FALSE(updater.hasMoreUpdates()); - layer->fakeLayerTextureUpdater()->clearUpdateCount(); + { + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + ccLayerTreeHost->updateLayers(updater); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(2, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_TRUE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(4, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_FALSE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + layer->pushPropertiesTo(layerImpl.get()); + } + ccLayerTreeHost->commitComplete(); + + // Checkerboard all tiles. + layer->invalidateRect(IntRect(0, 0, 300, 200)); + { + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + layer->pushPropertiesTo(layerImpl.get()); + } + ccLayerTreeHost->commitComplete(); + + // Partail update of 6 checkerboard tiles. + layer->invalidateRect(IntRect(50, 50, 200, 100)); + { + DebugScopedSetImplThread implThread; + OwnPtr<FakeCCTiledLayerImpl> layerImpl(adoptPtr(new FakeCCTiledLayerImpl(0))); + ccLayerTreeHost->updateLayers(updater); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(4, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_TRUE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + updater.update(0, &allocator, &copier, &uploader, 4); + EXPECT_EQ(2, layer->fakeLayerTextureUpdater()->updateCount()); + EXPECT_FALSE(updater.hasMoreUpdates()); + layer->fakeLayerTextureUpdater()->clearUpdateCount(); + layer->pushPropertiesTo(layerImpl.get()); + } ccLayerTreeHost->commitComplete(); ccLayerTreeHost->setRootLayer(0); @@ -787,13 +984,13 @@ TEST(TiledLayerChromiumTest, tilesPaintedWithoutOcclusion) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region occluded; + CCTextureUpdater updater; // The tile size is 100x100, so this invalidates and then paints two tiles. layer->setBounds(IntSize(100, 200)); layer->invalidateRect(IntRect(0, 0, 100, 200)); - layer->prepareToUpdate(IntRect(0, 0, 100, 200), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 100, 200), 0); EXPECT_EQ(2, layer->fakeLayerTextureUpdater()->prepareRectCount()); } @@ -801,97 +998,137 @@ TEST(TiledLayerChromiumTest, tilesPaintedWithOcclusion) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region occluded; + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; // The tile size is 100x100. layer->setBounds(IntSize(600, 600)); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); - occluded = IntRect(200, 200, 300, 100); + occluded.setOcclusion(IntRect(200, 200, 300, 100)); layer->setVisibleLayerRect(IntRect(IntPoint(), layer->bounds())); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(36-3, layer->fakeLayerTextureUpdater()->prepareRectCount()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); + EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); + layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); - occluded = IntRect(250, 200, 300, 100); + occluded.setOcclusion(IntRect(250, 200, 300, 100)); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(36-2, layer->fakeLayerTextureUpdater()->prepareRectCount()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000 + 340000, 1); + EXPECT_EQ(3 + 2, occluded.overdrawMetrics().tilesCulledForUpload()); + layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); - occluded = IntRect(250, 250, 300, 100); + occluded.setOcclusion(IntRect(250, 250, 300, 100)); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(36, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000 + 340000 + 360000, 1); + EXPECT_EQ(3 + 2, occluded.overdrawMetrics().tilesCulledForUpload()); } TEST(TiledLayerChromiumTest, tilesPaintedWithOcclusionAndVisiblityConstraints) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region occluded; + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; // The tile size is 100x100. layer->setBounds(IntSize(600, 600)); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); // The partially occluded tiles (by the 150 occlusion height) are visible beyond the occlusion, so not culled. - occluded = IntRect(200, 200, 300, 150); + occluded.setOcclusion(IntRect(200, 200, 300, 150)); layer->setVisibleLayerRect(IntRect(0, 0, 600, 360)); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); - EXPECT_EQ(36-3, layer->fakeLayerTextureUpdater()->prepareRectCount()); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 360), &occluded); + EXPECT_EQ(24-3, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 210000, 1); + EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); // Now the visible region stops at the edge of the occlusion so the partly visible tiles become fully occluded. - occluded = IntRect(200, 200, 300, 150); + occluded.setOcclusion(IntRect(200, 200, 300, 150)); layer->setVisibleLayerRect(IntRect(0, 0, 600, 350)); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); - EXPECT_EQ(36-6, layer->fakeLayerTextureUpdater()->prepareRectCount()); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 350), &occluded); + EXPECT_EQ(24-6, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 210000 + 180000, 1); + EXPECT_EQ(3 + 6, occluded.overdrawMetrics().tilesCulledForUpload()); layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); // Now the visible region is even smaller than the occlusion, it should have the same result. - occluded = IntRect(200, 200, 300, 150); + occluded.setOcclusion(IntRect(200, 200, 300, 150)); layer->setVisibleLayerRect(IntRect(0, 0, 600, 340)); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); - EXPECT_EQ(36-6, layer->fakeLayerTextureUpdater()->prepareRectCount()); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 340), &occluded); + EXPECT_EQ(24-6, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 210000 + 180000 + 180000, 1); + EXPECT_EQ(3 + 6 + 6, occluded.overdrawMetrics().tilesCulledForUpload()); + } TEST(TiledLayerChromiumTest, tilesNotPaintedWithoutInvalidation) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region occluded; + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; // The tile size is 100x100. layer->setBounds(IntSize(600, 600)); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); - occluded = IntRect(200, 200, 300, 100); + occluded.setOcclusion(IntRect(200, 200, 300, 100)); layer->setVisibleLayerRect(IntRect(0, 0, 600, 600)); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(36-3, layer->fakeLayerTextureUpdater()->prepareRectCount()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); + EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); + layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); - // Repaint without marking it dirty. - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + // Repaint without marking it dirty. The culled tiles remained dirty. + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(0, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); + EXPECT_EQ(6, occluded.overdrawMetrics().tilesCulledForUpload()); } TEST(TiledLayerChromiumTest, tilesPaintedWithOcclusionAndTransforms) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region occluded; + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; // The tile size is 100x100. @@ -901,19 +1138,25 @@ TEST(TiledLayerChromiumTest, tilesPaintedWithOcclusionAndTransforms) TransformationMatrix screenTransform; screenTransform.scale(0.5); layer->setScreenSpaceTransform(screenTransform); + layer->setDrawTransform(screenTransform * TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); - occluded = IntRect(100, 100, 150, 50); + occluded.setOcclusion(IntRect(100, 100, 150, 50)); layer->setVisibleLayerRect(IntRect(IntPoint(), layer->bounds())); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(36-3, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 330000, 1); + EXPECT_EQ(3, occluded.overdrawMetrics().tilesCulledForUpload()); } TEST(TiledLayerChromiumTest, tilesPaintedWithOcclusionAndScaling) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); - Region occluded; + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; // The tile size is 100x100. @@ -922,99 +1165,196 @@ TEST(TiledLayerChromiumTest, tilesPaintedWithOcclusionAndScaling) // pixels, which means none should be occluded. layer->setContentsScale(0.5); layer->setBounds(IntSize(600, 600)); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); - occluded = IntRect(200, 200, 300, 100); + occluded.setOcclusion(IntRect(200, 200, 300, 100)); layer->setVisibleLayerRect(IntRect(IntPoint(), layer->bounds())); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); // The content is half the size of the layer (so the number of tiles is fewer). // In this case, the content is 300x300, and since the tile size is 100, the // number of tiles 3x3. EXPECT_EQ(9, layer->fakeLayerTextureUpdater()->prepareRectCount()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 90000, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); // This makes sure the painting works when the content space is scaled to // a different layer space. In this case the occluded region catches the // blown up tiles. - occluded = IntRect(200, 200, 300, 200); + occluded.setOcclusion(IntRect(200, 200, 300, 200)); layer->setVisibleLayerRect(IntRect(IntPoint(), layer->bounds())); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(9-1, layer->fakeLayerTextureUpdater()->prepareRectCount()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 90000 + 80000, 1); + EXPECT_EQ(1, occluded.overdrawMetrics().tilesCulledForUpload()); + layer->fakeLayerTextureUpdater()->clearPrepareRectCount(); // This makes sure content scaling and transforms work together. TransformationMatrix screenTransform; screenTransform.scale(0.5); layer->setScreenSpaceTransform(screenTransform); + layer->setDrawTransform(screenTransform * TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); - occluded = IntRect(100, 100, 150, 100); + occluded.setOcclusion(IntRect(100, 100, 150, 100)); layer->setVisibleLayerRect(IntRect(IntPoint(), layer->bounds())); layer->invalidateRect(IntRect(0, 0, 600, 600)); - layer->prepareToUpdate(IntRect(0, 0, 600, 600), occluded); + layer->updateLayerRect(updater, IntRect(0, 0, 600, 600), &occluded); EXPECT_EQ(9-1, layer->fakeLayerTextureUpdater()->prepareRectCount()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 90000 + 80000 + 80000, 1); + EXPECT_EQ(1 + 1, occluded.overdrawMetrics().tilesCulledForUpload()); } -TEST(TiledLayerChromiumTest, opaqueContentsRegion) +TEST(TiledLayerChromiumTest, visibleContentOpaqueRegion) { OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; // The tile size is 100x100, so this invalidates and then paints two tiles in various ways. IntRect opaquePaintRect; Region opaqueContents; - Region noOcclusion; IntRect contentBounds = IntRect(0, 0, 100, 200); IntRect visibleBounds = IntRect(0, 0, 100, 150); layer->setBounds(contentBounds.size()); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); layer->setVisibleLayerRect(visibleBounds); layer->setDrawOpacity(1); - // If the layer doesn't paint opaque content, then the opaqueContentsRegion should be empty. - layer->fakeLayerTextureUpdater()->setOpaquePaintRect(opaquePaintRect); + // If the layer doesn't paint opaque content, then the visibleContentOpaqueRegion should be empty. + layer->fakeLayerTextureUpdater()->setOpaquePaintRect(IntRect()); layer->invalidateRect(contentBounds); - layer->prepareToUpdate(contentBounds, noOcclusion); - opaqueContents = layer->opaqueContentsRegion(); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); EXPECT_TRUE(opaqueContents.isEmpty()); - // opaqueContentsRegion should match the visible part of what is painted opaque. + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + + // visibleContentOpaqueRegion should match the visible part of what is painted opaque. opaquePaintRect = IntRect(10, 10, 90, 190); layer->fakeLayerTextureUpdater()->setOpaquePaintRect(opaquePaintRect); layer->invalidateRect(contentBounds); - layer->prepareToUpdate(contentBounds, noOcclusion); - opaqueContents = layer->opaqueContentsRegion(); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); EXPECT_EQ_RECT(intersection(opaquePaintRect, visibleBounds), opaqueContents.bounds()); EXPECT_EQ(1u, opaqueContents.rects().size()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + // If we paint again without invalidating, the same stuff should be opaque. layer->fakeLayerTextureUpdater()->setOpaquePaintRect(IntRect()); - layer->prepareToUpdate(contentBounds, noOcclusion); - opaqueContents = layer->opaqueContentsRegion(); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); EXPECT_EQ_RECT(intersection(opaquePaintRect, visibleBounds), opaqueContents.bounds()); EXPECT_EQ(1u, opaqueContents.rects().size()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + // If we repaint a non-opaque part of the tile, then it shouldn't lose its opaque-ness. And other tiles should // not be affected. layer->fakeLayerTextureUpdater()->setOpaquePaintRect(IntRect()); layer->invalidateRect(IntRect(0, 0, 1, 1)); - layer->prepareToUpdate(contentBounds, noOcclusion); - opaqueContents = layer->opaqueContentsRegion(); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); EXPECT_EQ_RECT(intersection(opaquePaintRect, visibleBounds), opaqueContents.bounds()); EXPECT_EQ(1u, opaqueContents.rects().size()); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2 + 1, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100 + 1, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + // If we repaint an opaque part of the tile, then it should lose its opaque-ness. But other tiles should still // not be affected. layer->fakeLayerTextureUpdater()->setOpaquePaintRect(IntRect()); layer->invalidateRect(IntRect(10, 10, 1, 1)); - layer->prepareToUpdate(contentBounds, noOcclusion); - opaqueContents = layer->opaqueContentsRegion(); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); EXPECT_EQ_RECT(intersection(IntRect(10, 100, 90, 100), visibleBounds), opaqueContents.bounds()); EXPECT_EQ(1u, opaqueContents.rects().size()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2 + 1 + 1, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100 + 1 + 1, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + + // No metrics are recorded in prepaint, so the values should not change from above. + layer->idleUpdateLayerRect(updater, contentBounds, &occluded); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 20000 * 2 + 1 + 1, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 17100, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 20000 + 20000 - 17100 + 1 + 1, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); +} + +TEST(TiledLayerChromiumTest, pixelsPaintedMetrics) +{ + OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024); + RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get())); + TestCCOcclusionTracker occluded; + CCTextureUpdater updater; + + // The tile size is 100x100, so this invalidates and then paints two tiles in various ways. + + IntRect opaquePaintRect; + Region opaqueContents; + + IntRect contentBounds = IntRect(0, 0, 100, 300); + IntRect visibleBounds = IntRect(0, 0, 100, 300); + + layer->setBounds(contentBounds.size()); + layer->setDrawTransform(TransformationMatrix(1, 0, 0, 1, layer->bounds().width() / 2.0, layer->bounds().height() / 2.0)); + layer->setVisibleLayerRect(visibleBounds); + layer->setDrawOpacity(1); + + // Invalidates and paints the whole layer. + layer->fakeLayerTextureUpdater()->setOpaquePaintRect(IntRect()); + layer->invalidateRect(contentBounds); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); + EXPECT_TRUE(opaqueContents.isEmpty()); + + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 30000, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 30000, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); + + // Invalidates an area on the top and bottom tile, which will cause us to paint the tile in the middle, + // even though it is not dirty and will not be uploaded. + layer->fakeLayerTextureUpdater()->setOpaquePaintRect(IntRect()); + layer->invalidateRect(IntRect(0, 0, 1, 1)); + layer->invalidateRect(IntRect(50, 200, 10, 10)); + layer->updateLayerRect(updater, contentBounds, &occluded); + opaqueContents = layer->visibleContentOpaqueRegion(); + EXPECT_TRUE(opaqueContents.isEmpty()); + + // The middle tile was painted even though not invalidated. + EXPECT_NEAR(occluded.overdrawMetrics().pixelsPainted(), 30000 + 60 * 210, 1); + // The pixels uploaded will not include the non-invalidated tile in the middle. + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedOpaque(), 0, 1); + EXPECT_NEAR(occluded.overdrawMetrics().pixelsUploadedTranslucent(), 30000 + 1 + 100, 1); + EXPECT_EQ(0, occluded.overdrawMetrics().tilesCulledForUpload()); } } // namespace diff --git a/Source/WebKit/chromium/tests/TilingDataTest.cpp b/Source/WebKit/chromium/tests/TilingDataTest.cpp index c29743f86..47c0361ea 100755 --- a/Source/WebKit/chromium/tests/TilingDataTest.cpp +++ b/Source/WebKit/chromium/tests/TilingDataTest.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "TilingData.h" +#include <wtf/Assertions.h> #include <gtest/gtest.h> @@ -38,385 +39,406 @@ using namespace WebCore; namespace { +class TestTiling : public TilingData { +public: + TestTiling(int maxTextureSize, int totalSizeX, int totalSizeY, bool hasBorderTexels) + : TilingData(maxTextureSize, totalSizeX, totalSizeY, hasBorderTexels) + { + } + + // This function only available for testing because of potential overflow. + int numTiles() const + { + int numTiles = numTilesX() * numTilesY(); + + // Assert no overflow. + EXPECT_GE(numTiles, 0); + if (numTiles > 0) + EXPECT_EQ(numTiles / numTilesX(), numTilesY()); + + return numTiles; + } +}; + TEST(TilingDataTest, numTiles_NoTiling) { - EXPECT_EQ(1, TilingData(16, 16, 16, false).numTiles()); - EXPECT_EQ(1, TilingData(16, 15, 15, true).numTiles()); - EXPECT_EQ(1, TilingData(16, 16, 16, true).numTiles()); - EXPECT_EQ(1, TilingData(16, 1, 16, false).numTiles()); - EXPECT_EQ(1, TilingData(15, 15, 15, true).numTiles()); + EXPECT_EQ(1, TestTiling(16, 16, 16, false).numTiles()); + EXPECT_EQ(1, TestTiling(16, 15, 15, true).numTiles()); + EXPECT_EQ(1, TestTiling(16, 16, 16, true).numTiles()); + EXPECT_EQ(1, TestTiling(16, 1, 16, false).numTiles()); + EXPECT_EQ(1, TestTiling(15, 15, 15, true).numTiles()); } TEST(TilingDataTest, numTiles_TilingNoBorders) { - EXPECT_EQ(0, TilingData(0, 0, 0, false).numTiles()); - EXPECT_EQ(0, TilingData(0, 4, 0, false).numTiles()); - EXPECT_EQ(0, TilingData(0, 0, 4, false).numTiles()); - EXPECT_EQ(0, TilingData(4, 4, 0, false).numTiles()); - EXPECT_EQ(0, TilingData(4, 0, 4, false).numTiles()); - EXPECT_EQ(0, TilingData(-8, 1, 1, false).numTiles()); - EXPECT_EQ(0, TilingData(-1, 1, 1, false).numTiles()); - EXPECT_EQ(0, TilingData(0, 1, 1, false).numTiles()); - - EXPECT_EQ(1, TilingData(1, 1, 1, false).numTiles()); - EXPECT_EQ(2, TilingData(1, 1, 2, false).numTiles()); - EXPECT_EQ(2, TilingData(1, 2, 1, false).numTiles()); - EXPECT_EQ(1, TilingData(2, 1, 1, false).numTiles()); - EXPECT_EQ(1, TilingData(2, 1, 2, false).numTiles()); - EXPECT_EQ(1, TilingData(2, 2, 1, false).numTiles()); - EXPECT_EQ(1, TilingData(2, 2, 2, false).numTiles()); - EXPECT_EQ(1, TilingData(3, 3, 3, false).numTiles()); - - EXPECT_EQ(1, TilingData(4, 1, 4, false).numTiles()); - EXPECT_EQ(1, TilingData(4, 2, 4, false).numTiles()); - EXPECT_EQ(1, TilingData(4, 3, 4, false).numTiles()); - EXPECT_EQ(1, TilingData(4, 4, 4, false).numTiles()); - EXPECT_EQ(2, TilingData(4, 5, 4, false).numTiles()); - EXPECT_EQ(2, TilingData(4, 6, 4, false).numTiles()); - EXPECT_EQ(2, TilingData(4, 7, 4, false).numTiles()); - EXPECT_EQ(2, TilingData(4, 8, 4, false).numTiles()); - EXPECT_EQ(3, TilingData(4, 9, 4, false).numTiles()); - EXPECT_EQ(3, TilingData(4, 10, 4, false).numTiles()); - EXPECT_EQ(3, TilingData(4, 11, 4, false).numTiles()); - - EXPECT_EQ(1, TilingData(5, 1, 5, false).numTiles()); - EXPECT_EQ(1, TilingData(5, 2, 5, false).numTiles()); - EXPECT_EQ(1, TilingData(5, 3, 5, false).numTiles()); - EXPECT_EQ(1, TilingData(5, 4, 5, false).numTiles()); - EXPECT_EQ(1, TilingData(5, 5, 5, false).numTiles()); - EXPECT_EQ(2, TilingData(5, 6, 5, false).numTiles()); - EXPECT_EQ(2, TilingData(5, 7, 5, false).numTiles()); - EXPECT_EQ(2, TilingData(5, 8, 5, false).numTiles()); - EXPECT_EQ(2, TilingData(5, 9, 5, false).numTiles()); - EXPECT_EQ(2, TilingData(5, 10, 5, false).numTiles()); - EXPECT_EQ(3, TilingData(5, 11, 5, false).numTiles()); - - EXPECT_EQ(1, TilingData(16, 16, 16, false).numTiles()); - EXPECT_EQ(1, TilingData(17, 16, 16, false).numTiles()); - EXPECT_EQ(4, TilingData(15, 16, 16, false).numTiles()); - EXPECT_EQ(4, TilingData(8, 16, 16, false).numTiles()); - EXPECT_EQ(6, TilingData(8, 17, 16, false).numTiles()); + EXPECT_EQ(0, TestTiling(0, 0, 0, false).numTiles()); + EXPECT_EQ(0, TestTiling(0, 4, 0, false).numTiles()); + EXPECT_EQ(0, TestTiling(0, 0, 4, false).numTiles()); + EXPECT_EQ(0, TestTiling(4, 4, 0, false).numTiles()); + EXPECT_EQ(0, TestTiling(4, 0, 4, false).numTiles()); + EXPECT_EQ(0, TestTiling(-8, 1, 1, false).numTiles()); + EXPECT_EQ(0, TestTiling(-1, 1, 1, false).numTiles()); + EXPECT_EQ(0, TestTiling(0, 1, 1, false).numTiles()); + + EXPECT_EQ(1, TestTiling(1, 1, 1, false).numTiles()); + EXPECT_EQ(2, TestTiling(1, 1, 2, false).numTiles()); + EXPECT_EQ(2, TestTiling(1, 2, 1, false).numTiles()); + EXPECT_EQ(1, TestTiling(2, 1, 1, false).numTiles()); + EXPECT_EQ(1, TestTiling(2, 1, 2, false).numTiles()); + EXPECT_EQ(1, TestTiling(2, 2, 1, false).numTiles()); + EXPECT_EQ(1, TestTiling(2, 2, 2, false).numTiles()); + EXPECT_EQ(1, TestTiling(3, 3, 3, false).numTiles()); + + EXPECT_EQ(1, TestTiling(4, 1, 4, false).numTiles()); + EXPECT_EQ(1, TestTiling(4, 2, 4, false).numTiles()); + EXPECT_EQ(1, TestTiling(4, 3, 4, false).numTiles()); + EXPECT_EQ(1, TestTiling(4, 4, 4, false).numTiles()); + EXPECT_EQ(2, TestTiling(4, 5, 4, false).numTiles()); + EXPECT_EQ(2, TestTiling(4, 6, 4, false).numTiles()); + EXPECT_EQ(2, TestTiling(4, 7, 4, false).numTiles()); + EXPECT_EQ(2, TestTiling(4, 8, 4, false).numTiles()); + EXPECT_EQ(3, TestTiling(4, 9, 4, false).numTiles()); + EXPECT_EQ(3, TestTiling(4, 10, 4, false).numTiles()); + EXPECT_EQ(3, TestTiling(4, 11, 4, false).numTiles()); + + EXPECT_EQ(1, TestTiling(5, 1, 5, false).numTiles()); + EXPECT_EQ(1, TestTiling(5, 2, 5, false).numTiles()); + EXPECT_EQ(1, TestTiling(5, 3, 5, false).numTiles()); + EXPECT_EQ(1, TestTiling(5, 4, 5, false).numTiles()); + EXPECT_EQ(1, TestTiling(5, 5, 5, false).numTiles()); + EXPECT_EQ(2, TestTiling(5, 6, 5, false).numTiles()); + EXPECT_EQ(2, TestTiling(5, 7, 5, false).numTiles()); + EXPECT_EQ(2, TestTiling(5, 8, 5, false).numTiles()); + EXPECT_EQ(2, TestTiling(5, 9, 5, false).numTiles()); + EXPECT_EQ(2, TestTiling(5, 10, 5, false).numTiles()); + EXPECT_EQ(3, TestTiling(5, 11, 5, false).numTiles()); + + EXPECT_EQ(1, TestTiling(16, 16, 16, false).numTiles()); + EXPECT_EQ(1, TestTiling(17, 16, 16, false).numTiles()); + EXPECT_EQ(4, TestTiling(15, 16, 16, false).numTiles()); + EXPECT_EQ(4, TestTiling(8, 16, 16, false).numTiles()); + EXPECT_EQ(6, TestTiling(8, 17, 16, false).numTiles()); } TEST(TilingDataTest, numTiles_TilingWithBorders) { - EXPECT_EQ(0, TilingData(0, 0, 0, true).numTiles()); - EXPECT_EQ(0, TilingData(0, 4, 0, true).numTiles()); - EXPECT_EQ(0, TilingData(0, 0, 4, true).numTiles()); - EXPECT_EQ(0, TilingData(4, 4, 0, true).numTiles()); - EXPECT_EQ(0, TilingData(4, 0, 4, true).numTiles()); - EXPECT_EQ(0, TilingData(-8, 1, 1, true).numTiles()); - EXPECT_EQ(0, TilingData(-1, 1, 1, true).numTiles()); - EXPECT_EQ(0, TilingData(0, 1, 1, true).numTiles()); - - EXPECT_EQ(1, TilingData(1, 1, 1, true).numTiles()); - EXPECT_EQ(0, TilingData(1, 1, 2, true).numTiles()); - EXPECT_EQ(0, TilingData(1, 2, 1, true).numTiles()); - EXPECT_EQ(1, TilingData(2, 1, 1, true).numTiles()); - EXPECT_EQ(1, TilingData(2, 1, 2, true).numTiles()); - EXPECT_EQ(1, TilingData(2, 2, 1, true).numTiles()); - EXPECT_EQ(1, TilingData(2, 2, 2, true).numTiles()); - - EXPECT_EQ(1, TilingData(3, 1, 3, true).numTiles()); - EXPECT_EQ(1, TilingData(3, 2, 3, true).numTiles()); - EXPECT_EQ(1, TilingData(3, 3, 3, true).numTiles()); - EXPECT_EQ(2, TilingData(3, 4, 3, true).numTiles()); - EXPECT_EQ(3, TilingData(3, 5, 3, true).numTiles()); - EXPECT_EQ(4, TilingData(3, 6, 3, true).numTiles()); - EXPECT_EQ(5, TilingData(3, 7, 3, true).numTiles()); - - EXPECT_EQ(1, TilingData(4, 1, 4, true).numTiles()); - EXPECT_EQ(1, TilingData(4, 2, 4, true).numTiles()); - EXPECT_EQ(1, TilingData(4, 3, 4, true).numTiles()); - EXPECT_EQ(1, TilingData(4, 4, 4, true).numTiles()); - EXPECT_EQ(2, TilingData(4, 5, 4, true).numTiles()); - EXPECT_EQ(2, TilingData(4, 6, 4, true).numTiles()); - EXPECT_EQ(3, TilingData(4, 7, 4, true).numTiles()); - EXPECT_EQ(3, TilingData(4, 8, 4, true).numTiles()); - EXPECT_EQ(4, TilingData(4, 9, 4, true).numTiles()); - EXPECT_EQ(4, TilingData(4, 10, 4, true).numTiles()); - EXPECT_EQ(5, TilingData(4, 11, 4, true).numTiles()); - - EXPECT_EQ(1, TilingData(5, 1, 5, true).numTiles()); - EXPECT_EQ(1, TilingData(5, 2, 5, true).numTiles()); - EXPECT_EQ(1, TilingData(5, 3, 5, true).numTiles()); - EXPECT_EQ(1, TilingData(5, 4, 5, true).numTiles()); - EXPECT_EQ(1, TilingData(5, 5, 5, true).numTiles()); - EXPECT_EQ(2, TilingData(5, 6, 5, true).numTiles()); - EXPECT_EQ(2, TilingData(5, 7, 5, true).numTiles()); - EXPECT_EQ(2, TilingData(5, 8, 5, true).numTiles()); - EXPECT_EQ(3, TilingData(5, 9, 5, true).numTiles()); - EXPECT_EQ(3, TilingData(5, 10, 5, true).numTiles()); - EXPECT_EQ(3, TilingData(5, 11, 5, true).numTiles()); + EXPECT_EQ(0, TestTiling(0, 0, 0, true).numTiles()); + EXPECT_EQ(0, TestTiling(0, 4, 0, true).numTiles()); + EXPECT_EQ(0, TestTiling(0, 0, 4, true).numTiles()); + EXPECT_EQ(0, TestTiling(4, 4, 0, true).numTiles()); + EXPECT_EQ(0, TestTiling(4, 0, 4, true).numTiles()); + EXPECT_EQ(0, TestTiling(-8, 1, 1, true).numTiles()); + EXPECT_EQ(0, TestTiling(-1, 1, 1, true).numTiles()); + EXPECT_EQ(0, TestTiling(0, 1, 1, true).numTiles()); + + EXPECT_EQ(1, TestTiling(1, 1, 1, true).numTiles()); + EXPECT_EQ(0, TestTiling(1, 1, 2, true).numTiles()); + EXPECT_EQ(0, TestTiling(1, 2, 1, true).numTiles()); + EXPECT_EQ(1, TestTiling(2, 1, 1, true).numTiles()); + EXPECT_EQ(1, TestTiling(2, 1, 2, true).numTiles()); + EXPECT_EQ(1, TestTiling(2, 2, 1, true).numTiles()); + EXPECT_EQ(1, TestTiling(2, 2, 2, true).numTiles()); + + EXPECT_EQ(1, TestTiling(3, 1, 3, true).numTiles()); + EXPECT_EQ(1, TestTiling(3, 2, 3, true).numTiles()); + EXPECT_EQ(1, TestTiling(3, 3, 3, true).numTiles()); + EXPECT_EQ(2, TestTiling(3, 4, 3, true).numTiles()); + EXPECT_EQ(3, TestTiling(3, 5, 3, true).numTiles()); + EXPECT_EQ(4, TestTiling(3, 6, 3, true).numTiles()); + EXPECT_EQ(5, TestTiling(3, 7, 3, true).numTiles()); + + EXPECT_EQ(1, TestTiling(4, 1, 4, true).numTiles()); + EXPECT_EQ(1, TestTiling(4, 2, 4, true).numTiles()); + EXPECT_EQ(1, TestTiling(4, 3, 4, true).numTiles()); + EXPECT_EQ(1, TestTiling(4, 4, 4, true).numTiles()); + EXPECT_EQ(2, TestTiling(4, 5, 4, true).numTiles()); + EXPECT_EQ(2, TestTiling(4, 6, 4, true).numTiles()); + EXPECT_EQ(3, TestTiling(4, 7, 4, true).numTiles()); + EXPECT_EQ(3, TestTiling(4, 8, 4, true).numTiles()); + EXPECT_EQ(4, TestTiling(4, 9, 4, true).numTiles()); + EXPECT_EQ(4, TestTiling(4, 10, 4, true).numTiles()); + EXPECT_EQ(5, TestTiling(4, 11, 4, true).numTiles()); + + EXPECT_EQ(1, TestTiling(5, 1, 5, true).numTiles()); + EXPECT_EQ(1, TestTiling(5, 2, 5, true).numTiles()); + EXPECT_EQ(1, TestTiling(5, 3, 5, true).numTiles()); + EXPECT_EQ(1, TestTiling(5, 4, 5, true).numTiles()); + EXPECT_EQ(1, TestTiling(5, 5, 5, true).numTiles()); + EXPECT_EQ(2, TestTiling(5, 6, 5, true).numTiles()); + EXPECT_EQ(2, TestTiling(5, 7, 5, true).numTiles()); + EXPECT_EQ(2, TestTiling(5, 8, 5, true).numTiles()); + EXPECT_EQ(3, TestTiling(5, 9, 5, true).numTiles()); + EXPECT_EQ(3, TestTiling(5, 10, 5, true).numTiles()); + EXPECT_EQ(3, TestTiling(5, 11, 5, true).numTiles()); } TEST(TilingDataTest, tileXIndexFromSrcCoord) { - EXPECT_EQ(0, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(2)); - EXPECT_EQ(1, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(3)); - EXPECT_EQ(1, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(4)); - EXPECT_EQ(1, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(5)); - EXPECT_EQ(2, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(6)); - EXPECT_EQ(2, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(7)); - EXPECT_EQ(2, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(8)); - EXPECT_EQ(3, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(9)); - EXPECT_EQ(3, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(10)); - EXPECT_EQ(3, TilingData(3, 10, 10, false).tileXIndexFromSrcCoord(11)); - - EXPECT_EQ(0, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(1, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(2)); - EXPECT_EQ(2, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(3)); - EXPECT_EQ(3, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(4)); - EXPECT_EQ(4, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(5)); - EXPECT_EQ(5, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(6)); - EXPECT_EQ(6, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(7)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(8)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(9)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(10)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileXIndexFromSrcCoord(11)); - - EXPECT_EQ(0, TilingData(1, 1, 1, false).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, false).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, false).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, false).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 3, 3, false).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, false).tileXIndexFromSrcCoord(2)); - - EXPECT_EQ(0, TilingData(3, 4, 3, false).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 4, 3, false).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 4, 3, false).tileXIndexFromSrcCoord(2)); - EXPECT_EQ(1, TilingData(3, 4, 3, false).tileXIndexFromSrcCoord(3)); - - EXPECT_EQ(0, TilingData(1, 1, 1, true).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, true).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, true).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, true).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 3, 3, true).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, true).tileXIndexFromSrcCoord(2)); - - EXPECT_EQ(0, TilingData(3, 4, 3, true).tileXIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 4, 3, true).tileXIndexFromSrcCoord(1)); - EXPECT_EQ(1, TilingData(3, 4, 3, true).tileXIndexFromSrcCoord(2)); - EXPECT_EQ(1, TilingData(3, 4, 3, true).tileXIndexFromSrcCoord(3)); + EXPECT_EQ(0, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(2)); + EXPECT_EQ(1, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(3)); + EXPECT_EQ(1, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(4)); + EXPECT_EQ(1, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(5)); + EXPECT_EQ(2, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(6)); + EXPECT_EQ(2, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(7)); + EXPECT_EQ(2, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(8)); + EXPECT_EQ(3, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(9)); + EXPECT_EQ(3, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(10)); + EXPECT_EQ(3, TestTiling(3, 10, 10, false).tileXIndexFromSrcCoord(11)); + + EXPECT_EQ(0, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(1, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(2)); + EXPECT_EQ(2, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(3)); + EXPECT_EQ(3, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(4)); + EXPECT_EQ(4, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(5)); + EXPECT_EQ(5, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(6)); + EXPECT_EQ(6, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(7)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(8)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(9)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(10)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileXIndexFromSrcCoord(11)); + + EXPECT_EQ(0, TestTiling(1, 1, 1, false).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, false).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, false).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, false).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 3, 3, false).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, false).tileXIndexFromSrcCoord(2)); + + EXPECT_EQ(0, TestTiling(3, 4, 3, false).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 4, 3, false).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 4, 3, false).tileXIndexFromSrcCoord(2)); + EXPECT_EQ(1, TestTiling(3, 4, 3, false).tileXIndexFromSrcCoord(3)); + + EXPECT_EQ(0, TestTiling(1, 1, 1, true).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, true).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, true).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, true).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 3, 3, true).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, true).tileXIndexFromSrcCoord(2)); + + EXPECT_EQ(0, TestTiling(3, 4, 3, true).tileXIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 4, 3, true).tileXIndexFromSrcCoord(1)); + EXPECT_EQ(1, TestTiling(3, 4, 3, true).tileXIndexFromSrcCoord(2)); + EXPECT_EQ(1, TestTiling(3, 4, 3, true).tileXIndexFromSrcCoord(3)); } TEST(TilingDataTest, tileYIndexFromSrcCoord) { - EXPECT_EQ(0, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(2)); - EXPECT_EQ(1, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(3)); - EXPECT_EQ(1, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(4)); - EXPECT_EQ(1, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(5)); - EXPECT_EQ(2, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(6)); - EXPECT_EQ(2, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(7)); - EXPECT_EQ(2, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(8)); - EXPECT_EQ(3, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(9)); - EXPECT_EQ(3, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(10)); - EXPECT_EQ(3, TilingData(3, 10, 10, false).tileYIndexFromSrcCoord(11)); - - EXPECT_EQ(0, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(1, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(2)); - EXPECT_EQ(2, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(3)); - EXPECT_EQ(3, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(4)); - EXPECT_EQ(4, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(5)); - EXPECT_EQ(5, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(6)); - EXPECT_EQ(6, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(7)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(8)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(9)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(10)); - EXPECT_EQ(7, TilingData(3, 10, 10, true).tileYIndexFromSrcCoord(11)); - - EXPECT_EQ(0, TilingData(1, 1, 1, false).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, false).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, false).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, false).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 3, 3, false).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, false).tileYIndexFromSrcCoord(2)); - - EXPECT_EQ(0, TilingData(3, 3, 4, false).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 3, 4, false).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 4, false).tileYIndexFromSrcCoord(2)); - EXPECT_EQ(1, TilingData(3, 3, 4, false).tileYIndexFromSrcCoord(3)); - - EXPECT_EQ(0, TilingData(1, 1, 1, true).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, true).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(2, 2, 2, true).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, true).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 3, 3, true).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(0, TilingData(3, 3, 3, true).tileYIndexFromSrcCoord(2)); - - EXPECT_EQ(0, TilingData(3, 3, 4, true).tileYIndexFromSrcCoord(0)); - EXPECT_EQ(0, TilingData(3, 3, 4, true).tileYIndexFromSrcCoord(1)); - EXPECT_EQ(1, TilingData(3, 3, 4, true).tileYIndexFromSrcCoord(2)); - EXPECT_EQ(1, TilingData(3, 3, 4, true).tileYIndexFromSrcCoord(3)); + EXPECT_EQ(0, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(2)); + EXPECT_EQ(1, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(3)); + EXPECT_EQ(1, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(4)); + EXPECT_EQ(1, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(5)); + EXPECT_EQ(2, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(6)); + EXPECT_EQ(2, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(7)); + EXPECT_EQ(2, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(8)); + EXPECT_EQ(3, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(9)); + EXPECT_EQ(3, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(10)); + EXPECT_EQ(3, TestTiling(3, 10, 10, false).tileYIndexFromSrcCoord(11)); + + EXPECT_EQ(0, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(1, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(2)); + EXPECT_EQ(2, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(3)); + EXPECT_EQ(3, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(4)); + EXPECT_EQ(4, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(5)); + EXPECT_EQ(5, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(6)); + EXPECT_EQ(6, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(7)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(8)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(9)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(10)); + EXPECT_EQ(7, TestTiling(3, 10, 10, true).tileYIndexFromSrcCoord(11)); + + EXPECT_EQ(0, TestTiling(1, 1, 1, false).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, false).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, false).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, false).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 3, 3, false).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, false).tileYIndexFromSrcCoord(2)); + + EXPECT_EQ(0, TestTiling(3, 3, 4, false).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 3, 4, false).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 4, false).tileYIndexFromSrcCoord(2)); + EXPECT_EQ(1, TestTiling(3, 3, 4, false).tileYIndexFromSrcCoord(3)); + + EXPECT_EQ(0, TestTiling(1, 1, 1, true).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, true).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(2, 2, 2, true).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, true).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 3, 3, true).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(0, TestTiling(3, 3, 3, true).tileYIndexFromSrcCoord(2)); + + EXPECT_EQ(0, TestTiling(3, 3, 4, true).tileYIndexFromSrcCoord(0)); + EXPECT_EQ(0, TestTiling(3, 3, 4, true).tileYIndexFromSrcCoord(1)); + EXPECT_EQ(1, TestTiling(3, 3, 4, true).tileYIndexFromSrcCoord(2)); + EXPECT_EQ(1, TestTiling(3, 3, 4, true).tileYIndexFromSrcCoord(3)); } TEST(TilingDataTest, tileSizeX) { - EXPECT_EQ(5, TilingData(5, 5, 5, false).tileSizeX(0)); - EXPECT_EQ(5, TilingData(5, 5, 5, true).tileSizeX(0)); - - EXPECT_EQ(5, TilingData(5, 6, 6, false).tileSizeX(0)); - EXPECT_EQ(1, TilingData(5, 6, 6, false).tileSizeX(1)); - EXPECT_EQ(4, TilingData(5, 6, 6, true).tileSizeX(0)); - EXPECT_EQ(2, TilingData(5, 6, 6, true).tileSizeX(1)); - - EXPECT_EQ(5, TilingData(5, 8, 8, false).tileSizeX(0)); - EXPECT_EQ(3, TilingData(5, 8, 8, false).tileSizeX(1)); - EXPECT_EQ(4, TilingData(5, 8, 8, true).tileSizeX(0)); - EXPECT_EQ(4, TilingData(5, 8, 8, true).tileSizeX(1)); - - EXPECT_EQ(5, TilingData(5, 10, 10, false).tileSizeX(0)); - EXPECT_EQ(5, TilingData(5, 10, 10, false).tileSizeX(1)); - EXPECT_EQ(4, TilingData(5, 10, 10, true).tileSizeX(0)); - EXPECT_EQ(3, TilingData(5, 10, 10, true).tileSizeX(1)); - EXPECT_EQ(3, TilingData(5, 10, 10, true).tileSizeX(2)); - - EXPECT_EQ(4, TilingData(5, 11, 11, true).tileSizeX(2)); - EXPECT_EQ(3, TilingData(5, 12, 12, true).tileSizeX(2)); + EXPECT_EQ(5, TestTiling(5, 5, 5, false).tileSizeX(0)); + EXPECT_EQ(5, TestTiling(5, 5, 5, true).tileSizeX(0)); + + EXPECT_EQ(5, TestTiling(5, 6, 6, false).tileSizeX(0)); + EXPECT_EQ(1, TestTiling(5, 6, 6, false).tileSizeX(1)); + EXPECT_EQ(4, TestTiling(5, 6, 6, true).tileSizeX(0)); + EXPECT_EQ(2, TestTiling(5, 6, 6, true).tileSizeX(1)); + + EXPECT_EQ(5, TestTiling(5, 8, 8, false).tileSizeX(0)); + EXPECT_EQ(3, TestTiling(5, 8, 8, false).tileSizeX(1)); + EXPECT_EQ(4, TestTiling(5, 8, 8, true).tileSizeX(0)); + EXPECT_EQ(4, TestTiling(5, 8, 8, true).tileSizeX(1)); + + EXPECT_EQ(5, TestTiling(5, 10, 10, false).tileSizeX(0)); + EXPECT_EQ(5, TestTiling(5, 10, 10, false).tileSizeX(1)); + EXPECT_EQ(4, TestTiling(5, 10, 10, true).tileSizeX(0)); + EXPECT_EQ(3, TestTiling(5, 10, 10, true).tileSizeX(1)); + EXPECT_EQ(3, TestTiling(5, 10, 10, true).tileSizeX(2)); + + EXPECT_EQ(4, TestTiling(5, 11, 11, true).tileSizeX(2)); + EXPECT_EQ(3, TestTiling(5, 12, 12, true).tileSizeX(2)); } TEST(TilingDataTest, tileSizeY) { - EXPECT_EQ(5, TilingData(5, 5, 5, false).tileSizeY(0)); - EXPECT_EQ(5, TilingData(5, 5, 5, true).tileSizeY(0)); - - EXPECT_EQ(5, TilingData(5, 6, 6, false).tileSizeY(0)); - EXPECT_EQ(1, TilingData(5, 6, 6, false).tileSizeY(1)); - EXPECT_EQ(4, TilingData(5, 6, 6, true).tileSizeY(0)); - EXPECT_EQ(2, TilingData(5, 6, 6, true).tileSizeY(1)); - - EXPECT_EQ(5, TilingData(5, 8, 8, false).tileSizeY(0)); - EXPECT_EQ(3, TilingData(5, 8, 8, false).tileSizeY(1)); - EXPECT_EQ(4, TilingData(5, 8, 8, true).tileSizeY(0)); - EXPECT_EQ(4, TilingData(5, 8, 8, true).tileSizeY(1)); - - EXPECT_EQ(5, TilingData(5, 10, 10, false).tileSizeY(0)); - EXPECT_EQ(5, TilingData(5, 10, 10, false).tileSizeY(1)); - EXPECT_EQ(4, TilingData(5, 10, 10, true).tileSizeY(0)); - EXPECT_EQ(3, TilingData(5, 10, 10, true).tileSizeY(1)); - EXPECT_EQ(3, TilingData(5, 10, 10, true).tileSizeY(2)); - - EXPECT_EQ(4, TilingData(5, 11, 11, true).tileSizeY(2)); - EXPECT_EQ(3, TilingData(5, 12, 12, true).tileSizeY(2)); + EXPECT_EQ(5, TestTiling(5, 5, 5, false).tileSizeY(0)); + EXPECT_EQ(5, TestTiling(5, 5, 5, true).tileSizeY(0)); + + EXPECT_EQ(5, TestTiling(5, 6, 6, false).tileSizeY(0)); + EXPECT_EQ(1, TestTiling(5, 6, 6, false).tileSizeY(1)); + EXPECT_EQ(4, TestTiling(5, 6, 6, true).tileSizeY(0)); + EXPECT_EQ(2, TestTiling(5, 6, 6, true).tileSizeY(1)); + + EXPECT_EQ(5, TestTiling(5, 8, 8, false).tileSizeY(0)); + EXPECT_EQ(3, TestTiling(5, 8, 8, false).tileSizeY(1)); + EXPECT_EQ(4, TestTiling(5, 8, 8, true).tileSizeY(0)); + EXPECT_EQ(4, TestTiling(5, 8, 8, true).tileSizeY(1)); + + EXPECT_EQ(5, TestTiling(5, 10, 10, false).tileSizeY(0)); + EXPECT_EQ(5, TestTiling(5, 10, 10, false).tileSizeY(1)); + EXPECT_EQ(4, TestTiling(5, 10, 10, true).tileSizeY(0)); + EXPECT_EQ(3, TestTiling(5, 10, 10, true).tileSizeY(1)); + EXPECT_EQ(3, TestTiling(5, 10, 10, true).tileSizeY(2)); + + EXPECT_EQ(4, TestTiling(5, 11, 11, true).tileSizeY(2)); + EXPECT_EQ(3, TestTiling(5, 12, 12, true).tileSizeY(2)); } TEST(TilingDataTest, tileSizeX_and_tilePositionX) { // Single tile cases: - EXPECT_EQ(1, TilingData(3, 1, 1, false).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 1, 1, false).tilePositionX(0)); - EXPECT_EQ(1, TilingData(3, 1, 100, false).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 1, 100, false).tilePositionX(0)); - EXPECT_EQ(3, TilingData(3, 3, 1, false).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 3, 1, false).tilePositionX(0)); - EXPECT_EQ(3, TilingData(3, 3, 100, false).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 3, 100, false).tilePositionX(0)); - EXPECT_EQ(1, TilingData(3, 1, 1, true).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 1, 1, true).tilePositionX(0)); - EXPECT_EQ(1, TilingData(3, 1, 100, true).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 1, 100, true).tilePositionX(0)); - EXPECT_EQ(3, TilingData(3, 3, 1, true).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 3, 1, true).tilePositionX(0)); - EXPECT_EQ(3, TilingData(3, 3, 100, true).tileSizeX(0)); - EXPECT_EQ(0, TilingData(3, 3, 100, true).tilePositionX(0)); + EXPECT_EQ(1, TestTiling(3, 1, 1, false).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 1, 1, false).tilePositionX(0)); + EXPECT_EQ(1, TestTiling(3, 1, 100, false).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 1, 100, false).tilePositionX(0)); + EXPECT_EQ(3, TestTiling(3, 3, 1, false).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 3, 1, false).tilePositionX(0)); + EXPECT_EQ(3, TestTiling(3, 3, 100, false).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 3, 100, false).tilePositionX(0)); + EXPECT_EQ(1, TestTiling(3, 1, 1, true).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 1, 1, true).tilePositionX(0)); + EXPECT_EQ(1, TestTiling(3, 1, 100, true).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 1, 100, true).tilePositionX(0)); + EXPECT_EQ(3, TestTiling(3, 3, 1, true).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 3, 1, true).tilePositionX(0)); + EXPECT_EQ(3, TestTiling(3, 3, 100, true).tileSizeX(0)); + EXPECT_EQ(0, TestTiling(3, 3, 100, true).tilePositionX(0)); // Multiple tiles: // no border // positions 0, 3 - EXPECT_EQ(2, TilingData(3, 6, 1, false).numTiles()); - EXPECT_EQ(3, TilingData(3, 6, 1, false).tileSizeX(0)); - EXPECT_EQ(3, TilingData(3, 6, 1, false).tileSizeX(1)); - EXPECT_EQ(0, TilingData(3, 6, 1, false).tilePositionX(0)); - EXPECT_EQ(3, TilingData(3, 6, 1, false).tilePositionX(1)); - EXPECT_EQ(3, TilingData(3, 6, 100, false).tileSizeX(0)); - EXPECT_EQ(3, TilingData(3, 6, 100, false).tileSizeX(1)); - EXPECT_EQ(0, TilingData(3, 6, 100, false).tilePositionX(0)); - EXPECT_EQ(3, TilingData(3, 6, 100, false).tilePositionX(1)); + EXPECT_EQ(2, TestTiling(3, 6, 1, false).numTiles()); + EXPECT_EQ(3, TestTiling(3, 6, 1, false).tileSizeX(0)); + EXPECT_EQ(3, TestTiling(3, 6, 1, false).tileSizeX(1)); + EXPECT_EQ(0, TestTiling(3, 6, 1, false).tilePositionX(0)); + EXPECT_EQ(3, TestTiling(3, 6, 1, false).tilePositionX(1)); + EXPECT_EQ(3, TestTiling(3, 6, 100, false).tileSizeX(0)); + EXPECT_EQ(3, TestTiling(3, 6, 100, false).tileSizeX(1)); + EXPECT_EQ(0, TestTiling(3, 6, 100, false).tilePositionX(0)); + EXPECT_EQ(3, TestTiling(3, 6, 100, false).tilePositionX(1)); // Multiple tiles: // with border // positions 0, 2, 3, 4 - EXPECT_EQ(4, TilingData(3, 6, 1, true).numTiles()); - EXPECT_EQ(2, TilingData(3, 6, 1, true).tileSizeX(0)); - EXPECT_EQ(1, TilingData(3, 6, 1, true).tileSizeX(1)); - EXPECT_EQ(1, TilingData(3, 6, 1, true).tileSizeX(2)); - EXPECT_EQ(2, TilingData(3, 6, 1, true).tileSizeX(3)); - EXPECT_EQ(0, TilingData(3, 6, 1, true).tilePositionX(0)); - EXPECT_EQ(2, TilingData(3, 6, 1, true).tilePositionX(1)); - EXPECT_EQ(3, TilingData(3, 6, 1, true).tilePositionX(2)); - EXPECT_EQ(4, TilingData(3, 6, 1, true).tilePositionX(3)); - EXPECT_EQ(2, TilingData(3, 6, 100, true).tileSizeX(0)); - EXPECT_EQ(1, TilingData(3, 6, 100, true).tileSizeX(1)); - EXPECT_EQ(1, TilingData(3, 6, 100, true).tileSizeX(2)); - EXPECT_EQ(2, TilingData(3, 6, 100, true).tileSizeX(3)); - EXPECT_EQ(0, TilingData(3, 6, 100, true).tilePositionX(0)); - EXPECT_EQ(2, TilingData(3, 6, 100, true).tilePositionX(1)); - EXPECT_EQ(3, TilingData(3, 6, 100, true).tilePositionX(2)); - EXPECT_EQ(4, TilingData(3, 6, 100, true).tilePositionX(3)); + EXPECT_EQ(4, TestTiling(3, 6, 1, true).numTiles()); + EXPECT_EQ(2, TestTiling(3, 6, 1, true).tileSizeX(0)); + EXPECT_EQ(1, TestTiling(3, 6, 1, true).tileSizeX(1)); + EXPECT_EQ(1, TestTiling(3, 6, 1, true).tileSizeX(2)); + EXPECT_EQ(2, TestTiling(3, 6, 1, true).tileSizeX(3)); + EXPECT_EQ(0, TestTiling(3, 6, 1, true).tilePositionX(0)); + EXPECT_EQ(2, TestTiling(3, 6, 1, true).tilePositionX(1)); + EXPECT_EQ(3, TestTiling(3, 6, 1, true).tilePositionX(2)); + EXPECT_EQ(4, TestTiling(3, 6, 1, true).tilePositionX(3)); + EXPECT_EQ(2, TestTiling(3, 6, 100, true).tileSizeX(0)); + EXPECT_EQ(1, TestTiling(3, 6, 100, true).tileSizeX(1)); + EXPECT_EQ(1, TestTiling(3, 6, 100, true).tileSizeX(2)); + EXPECT_EQ(2, TestTiling(3, 6, 100, true).tileSizeX(3)); + EXPECT_EQ(0, TestTiling(3, 6, 100, true).tilePositionX(0)); + EXPECT_EQ(2, TestTiling(3, 6, 100, true).tilePositionX(1)); + EXPECT_EQ(3, TestTiling(3, 6, 100, true).tilePositionX(2)); + EXPECT_EQ(4, TestTiling(3, 6, 100, true).tilePositionX(3)); } TEST(TilingDataTest, tileSizeY_and_tilePositionY) { // Single tile cases: - EXPECT_EQ(1, TilingData(3, 1, 1, false).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 1, 1, false).tilePositionY(0)); - EXPECT_EQ(1, TilingData(3, 100, 1, false).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 100, 1, false).tilePositionY(0)); - EXPECT_EQ(3, TilingData(3, 1, 3, false).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 1, 3, false).tilePositionY(0)); - EXPECT_EQ(3, TilingData(3, 100, 3, false).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 100, 3, false).tilePositionY(0)); - EXPECT_EQ(1, TilingData(3, 1, 1, true).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 1, 1, true).tilePositionY(0)); - EXPECT_EQ(1, TilingData(3, 100, 1, true).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 100, 1, true).tilePositionY(0)); - EXPECT_EQ(3, TilingData(3, 1, 3, true).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 1, 3, true).tilePositionY(0)); - EXPECT_EQ(3, TilingData(3, 100, 3, true).tileSizeY(0)); - EXPECT_EQ(0, TilingData(3, 100, 3, true).tilePositionY(0)); + EXPECT_EQ(1, TestTiling(3, 1, 1, false).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 1, 1, false).tilePositionY(0)); + EXPECT_EQ(1, TestTiling(3, 100, 1, false).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 100, 1, false).tilePositionY(0)); + EXPECT_EQ(3, TestTiling(3, 1, 3, false).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 1, 3, false).tilePositionY(0)); + EXPECT_EQ(3, TestTiling(3, 100, 3, false).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 100, 3, false).tilePositionY(0)); + EXPECT_EQ(1, TestTiling(3, 1, 1, true).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 1, 1, true).tilePositionY(0)); + EXPECT_EQ(1, TestTiling(3, 100, 1, true).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 100, 1, true).tilePositionY(0)); + EXPECT_EQ(3, TestTiling(3, 1, 3, true).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 1, 3, true).tilePositionY(0)); + EXPECT_EQ(3, TestTiling(3, 100, 3, true).tileSizeY(0)); + EXPECT_EQ(0, TestTiling(3, 100, 3, true).tilePositionY(0)); // Multiple tiles: // no border // positions 0, 3 - EXPECT_EQ(2, TilingData(3, 1, 6, false).numTiles()); - EXPECT_EQ(3, TilingData(3, 1, 6, false).tileSizeY(0)); - EXPECT_EQ(3, TilingData(3, 1, 6, false).tileSizeY(1)); - EXPECT_EQ(0, TilingData(3, 1, 6, false).tilePositionY(0)); - EXPECT_EQ(3, TilingData(3, 1, 6, false).tilePositionY(1)); - EXPECT_EQ(3, TilingData(3, 100, 6, false).tileSizeY(0)); - EXPECT_EQ(3, TilingData(3, 100, 6, false).tileSizeY(1)); - EXPECT_EQ(0, TilingData(3, 100, 6, false).tilePositionY(0)); - EXPECT_EQ(3, TilingData(3, 100, 6, false).tilePositionY(1)); + EXPECT_EQ(2, TestTiling(3, 1, 6, false).numTiles()); + EXPECT_EQ(3, TestTiling(3, 1, 6, false).tileSizeY(0)); + EXPECT_EQ(3, TestTiling(3, 1, 6, false).tileSizeY(1)); + EXPECT_EQ(0, TestTiling(3, 1, 6, false).tilePositionY(0)); + EXPECT_EQ(3, TestTiling(3, 1, 6, false).tilePositionY(1)); + EXPECT_EQ(3, TestTiling(3, 100, 6, false).tileSizeY(0)); + EXPECT_EQ(3, TestTiling(3, 100, 6, false).tileSizeY(1)); + EXPECT_EQ(0, TestTiling(3, 100, 6, false).tilePositionY(0)); + EXPECT_EQ(3, TestTiling(3, 100, 6, false).tilePositionY(1)); // Multiple tiles: // with border // positions 0, 2, 3, 4 - EXPECT_EQ(4, TilingData(3, 1, 6, true).numTiles()); - EXPECT_EQ(2, TilingData(3, 1, 6, true).tileSizeY(0)); - EXPECT_EQ(1, TilingData(3, 1, 6, true).tileSizeY(1)); - EXPECT_EQ(1, TilingData(3, 1, 6, true).tileSizeY(2)); - EXPECT_EQ(2, TilingData(3, 1, 6, true).tileSizeY(3)); - EXPECT_EQ(0, TilingData(3, 1, 6, true).tilePositionY(0)); - EXPECT_EQ(2, TilingData(3, 1, 6, true).tilePositionY(1)); - EXPECT_EQ(3, TilingData(3, 1, 6, true).tilePositionY(2)); - EXPECT_EQ(4, TilingData(3, 1, 6, true).tilePositionY(3)); - EXPECT_EQ(2, TilingData(3, 100, 6, true).tileSizeY(0)); - EXPECT_EQ(1, TilingData(3, 100, 6, true).tileSizeY(1)); - EXPECT_EQ(1, TilingData(3, 100, 6, true).tileSizeY(2)); - EXPECT_EQ(2, TilingData(3, 100, 6, true).tileSizeY(3)); - EXPECT_EQ(0, TilingData(3, 100, 6, true).tilePositionY(0)); - EXPECT_EQ(2, TilingData(3, 100, 6, true).tilePositionY(1)); - EXPECT_EQ(3, TilingData(3, 100, 6, true).tilePositionY(2)); - EXPECT_EQ(4, TilingData(3, 100, 6, true).tilePositionY(3)); + EXPECT_EQ(4, TestTiling(3, 1, 6, true).numTiles()); + EXPECT_EQ(2, TestTiling(3, 1, 6, true).tileSizeY(0)); + EXPECT_EQ(1, TestTiling(3, 1, 6, true).tileSizeY(1)); + EXPECT_EQ(1, TestTiling(3, 1, 6, true).tileSizeY(2)); + EXPECT_EQ(2, TestTiling(3, 1, 6, true).tileSizeY(3)); + EXPECT_EQ(0, TestTiling(3, 1, 6, true).tilePositionY(0)); + EXPECT_EQ(2, TestTiling(3, 1, 6, true).tilePositionY(1)); + EXPECT_EQ(3, TestTiling(3, 1, 6, true).tilePositionY(2)); + EXPECT_EQ(4, TestTiling(3, 1, 6, true).tilePositionY(3)); + EXPECT_EQ(2, TestTiling(3, 100, 6, true).tileSizeY(0)); + EXPECT_EQ(1, TestTiling(3, 100, 6, true).tileSizeY(1)); + EXPECT_EQ(1, TestTiling(3, 100, 6, true).tileSizeY(2)); + EXPECT_EQ(2, TestTiling(3, 100, 6, true).tileSizeY(3)); + EXPECT_EQ(0, TestTiling(3, 100, 6, true).tilePositionY(0)); + EXPECT_EQ(2, TestTiling(3, 100, 6, true).tilePositionY(1)); + EXPECT_EQ(3, TestTiling(3, 100, 6, true).tilePositionY(2)); + EXPECT_EQ(4, TestTiling(3, 100, 6, true).tilePositionY(3)); } TEST(TilingDataTest, setTotalSize) { - TilingData data(5, 5, 5, false); + TestTiling data(5, 5, 5, false); EXPECT_EQ(5, data.totalSizeX()); EXPECT_EQ(5, data.totalSizeY()); EXPECT_EQ(1, data.numTilesX()); @@ -446,7 +468,7 @@ TEST(TilingDataTest, setTotalSize) TEST(TilingDataTest, setMaxTextureSizeNoBorders) { - TilingData data(8, 16, 32, false); + TestTiling data(8, 16, 32, false); EXPECT_EQ(2, data.numTilesX()); EXPECT_EQ(4, data.numTilesY()); @@ -468,7 +490,7 @@ TEST(TilingDataTest, setMaxTextureSizeNoBorders) TEST(TilingDataTest, setMaxTextureSizeBorders) { - TilingData data(8, 16, 32, true); + TestTiling data(8, 16, 32, true); EXPECT_EQ(3, data.numTilesX()); EXPECT_EQ(5, data.numTilesY()); @@ -491,8 +513,8 @@ TEST(TilingDataTest, setMaxTextureSizeBorders) TEST(TilingDataTest, assignment) { { - TilingData source(8, 16, 32, true); - TilingData dest = source; + TestTiling source(8, 16, 32, true); + TestTiling dest = source; EXPECT_EQ(source.borderTexels(), dest.borderTexels()); EXPECT_EQ(source.maxTextureSize(), dest.maxTextureSize()); EXPECT_EQ(source.numTilesX(), dest.numTilesX()); @@ -501,8 +523,8 @@ TEST(TilingDataTest, assignment) EXPECT_EQ(source.totalSizeY(), dest.totalSizeY()); } { - TilingData source(3, 6, 100, false); - TilingData dest(source); + TestTiling source(3, 6, 100, false); + TestTiling dest(source); EXPECT_EQ(source.borderTexels(), dest.borderTexels()); EXPECT_EQ(source.maxTextureSize(), dest.maxTextureSize()); EXPECT_EQ(source.numTilesX(), dest.numTilesX()); @@ -514,7 +536,7 @@ TEST(TilingDataTest, assignment) TEST(TilingDataTest, setBorderTexels) { - TilingData data(8, 16, 32, false); + TestTiling data(8, 16, 32, false); EXPECT_EQ(2, data.numTilesX()); EXPECT_EQ(4, data.numTilesY()); diff --git a/Source/WebKit/chromium/tests/TransparencyWinTest.cpp b/Source/WebKit/chromium/tests/TransparencyWinTest.cpp index 5052bb166..ae3090d8c 100644 --- a/Source/WebKit/chromium/tests/TransparencyWinTest.cpp +++ b/Source/WebKit/chromium/tests/TransparencyWinTest.cpp @@ -395,6 +395,66 @@ TEST(TransparencyWin, TranslateOpaqueCompositeLayer) EXPECT_EQ(green, getPixelAt(src->context(), 15, 7)); } +static void testClippedLayerKeepTransform(TransparencyWin::LayerMode layerMode) +{ + // Fill with white. + OwnPtr<ImageBuffer> src(ImageBuffer::create(IntSize(16, 16), 1, ColorSpaceDeviceRGB)); + Color white(0xFFFFFFFF); + FloatRect fullRect(0, 0, 16, 16); + src->context()->fillRect(fullRect, white, ColorSpaceDeviceRGB); + + IntRect clipRect(IntPoint(11, 5), IntSize(1, 1)); + src->context()->clip(clipRect); + + // Scroll down by 6 (coordinate system goes up). + src->context()->save(); + src->context()->translate(0, -6); + + Color red(0xFFFF0000); + Color green(0xFF00FF00); + { + // The transparency layer after translation will be @ (0, -6) with + // a size that would be too large to handle unclipped. + TransparencyWin helper; + helper.init(src->context(), + layerMode, + TransparencyWin::KeepTransform, + IntRect(0, 0, INT_MAX, INT_MAX)); + + // Draw a green pixel at (11, 11). This should be within the clip rect + // and at (11, 5) after the transform. + FloatRect greenRect(11, 11, 1, 1); + helper.context()->fillRect(greenRect, green, ColorSpaceDeviceRGB); + + // Draw a red pixel at (9, 9). This should be outside the clip rect + // and not drawn. + FloatRect redRect(9, 9, 1, 1); + helper.context()->fillRect(redRect, red, ColorSpaceDeviceRGB); + helper.composite(); + } + + src->context()->restore(); + + // Verify green pixel got drawn in clip rect and red pixel got clipped. + EXPECT_EQ(green, getPixelAt(src->context(), 11, 5)); + EXPECT_EQ(white, getPixelAt(src->context(), 9, 3)); +} + +TEST(TransparencyWin, ClippedKeepTransformNoLayer) +{ + testClippedLayerKeepTransform(TransparencyWin::NoLayer); +} + +TEST(TransparencyWin, ClippedKeepTransformOpaqueCompositeLayer) +{ + testClippedLayerKeepTransform(TransparencyWin::OpaqueCompositeLayer); +} + +TEST(TransparencyWin, ClippedKeepTransformWhiteLayer) +{ + testClippedLayerKeepTransform(TransparencyWin::WhiteLayer); +} + // Same as OpaqueCompositeLayer, but the canvas has a rotation applied. This // tests that the propert transform is applied to the copied layer. TEST(TransparencyWin, RotateOpaqueCompositeLayer) diff --git a/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp b/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp index eb3850dab..098dd2731 100644 --- a/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp +++ b/Source/WebKit/chromium/tests/TreeSynchronizerTest.cpp @@ -28,6 +28,7 @@ #include "CCAnimationTestCommon.h" #include "LayerChromium.h" +#include "Region.h" #include "cc/CCLayerAnimationController.h" #include "cc/CCLayerImpl.h" #include "cc/CCProxy.h" @@ -72,12 +73,12 @@ public: virtual ~MockLayerChromium() { } - virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() + virtual PassOwnPtr<CCLayerImpl> createCCLayerImpl() OVERRIDE { return MockCCLayerImpl::create(m_layerId); } - virtual void pushPropertiesTo(CCLayerImpl* ccLayer) + virtual void pushPropertiesTo(CCLayerImpl* ccLayer) OVERRIDE { LayerChromium::pushPropertiesTo(ccLayer); @@ -96,22 +97,23 @@ private: class FakeLayerAnimationController : public CCLayerAnimationController { public: - static PassOwnPtr<FakeLayerAnimationController> create() + static PassOwnPtr<FakeLayerAnimationController> create(CCLayerAnimationControllerClient* client) { - return adoptPtr(new FakeLayerAnimationController); + return adoptPtr(new FakeLayerAnimationController(client)); } bool synchronizedAnimations() const { return m_synchronizedAnimations; } private: - FakeLayerAnimationController() - : m_synchronizedAnimations(false) + explicit FakeLayerAnimationController(CCLayerAnimationControllerClient* client) + : CCLayerAnimationController(client) + , m_synchronizedAnimations(false) { } - virtual void synchronizeAnimations(CCLayerAnimationControllerImpl* controllerImpl) + virtual void pushAnimationUpdatesTo(CCLayerAnimationController* controllerImpl) { - CCLayerAnimationController::synchronizeAnimations(controllerImpl); + CCLayerAnimationController::pushAnimationUpdatesTo(controllerImpl); m_synchronizedAnimations = true; } @@ -125,6 +127,8 @@ void expectTreesAreIdentical(LayerChromium* layer, CCLayerImpl* ccLayer) EXPECT_EQ(layer->id(), ccLayer->id()); + EXPECT_EQ(layer->nonFastScrollableRegion(), ccLayer->nonFastScrollableRegion()); + ASSERT_EQ(!!layer->maskLayer(), !!ccLayer->maskLayer()); if (layer->maskLayer()) expectTreesAreIdentical(layer->maskLayer(), ccLayer->maskLayer()); @@ -142,6 +146,17 @@ void expectTreesAreIdentical(LayerChromium* layer, CCLayerImpl* ccLayer) expectTreesAreIdentical(layerChildren[i].get(), ccLayerChildren[i].get()); } +// Attempts to synchronizes a null tree. This should not crash, and should +// return a null tree. +TEST(TreeSynchronizerTest, syncNullTree) +{ + DebugScopedSetImplThread impl; + + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(0, nullptr); + + EXPECT_TRUE(!ccLayerTreeRoot.get()); +} + // Constructs a very simple tree and synchronizes it without trying to reuse any preexisting layers. TEST(TreeSynchronizerTest, syncSimpleTreeFromEmpty) { @@ -182,6 +197,34 @@ TEST(TreeSynchronizerTest, syncSimpleTreeReusingLayers) EXPECT_EQ(secondCCLayerId, ccLayerDestructionList[0]); } +// Constructs a very simple tree and checks that a stacking-order change is tracked properly. +TEST(TreeSynchronizerTest, syncSimpleTreeAndTrackStackingOrderChange) +{ + DebugScopedSetImplThread impl; + Vector<int> ccLayerDestructionList; + + // Set up the tree and sync once. child2 needs to be synced here, too, even though we + // remove it to set up the intended scenario. + RefPtr<LayerChromium> layerTreeRoot = MockLayerChromium::create(&ccLayerDestructionList); + RefPtr<LayerChromium> child2 = MockLayerChromium::create(&ccLayerDestructionList); + layerTreeRoot->addChild(MockLayerChromium::create(&ccLayerDestructionList)); + layerTreeRoot->addChild(child2); + OwnPtr<CCLayerImpl> ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), nullptr); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + ccLayerTreeRoot->resetAllChangeTrackingForSubtree(); + + // re-insert the layer and sync again. + child2->removeFromParent(); + layerTreeRoot->addChild(child2); + ccLayerTreeRoot = TreeSynchronizer::synchronizeTrees(layerTreeRoot.get(), ccLayerTreeRoot.release()); + expectTreesAreIdentical(layerTreeRoot.get(), ccLayerTreeRoot.get()); + + // Check that the impl thread properly tracked the change. + EXPECT_FALSE(ccLayerTreeRoot->layerPropertyChanged()); + EXPECT_FALSE(ccLayerTreeRoot->children()[0]->layerPropertyChanged()); + EXPECT_TRUE(ccLayerTreeRoot->children()[1]->layerPropertyChanged()); +} + TEST(TreeSynchronizerTest, syncSimpleTreeAndProperties) { DebugScopedSetImplThread impl; @@ -190,10 +233,10 @@ TEST(TreeSynchronizerTest, syncSimpleTreeAndProperties) layerTreeRoot->addChild(LayerChromium::create()); // Pick some random properties to set. The values are not important, we're just testing that at least some properties are making it through. - FloatPoint rootPosition = FloatPoint(2.3, 7.4); + FloatPoint rootPosition = FloatPoint(2.3f, 7.4f); layerTreeRoot->setPosition(rootPosition); - float firstChildOpacity = 0.25; + float firstChildOpacity = 0.25f; layerTreeRoot->children()[0]->setOpacity(firstChildOpacity); IntSize secondChildBounds = IntSize(25, 53); @@ -339,7 +382,8 @@ TEST(TreeSynchronizerTest, synchronizeAnimations) DebugScopedSetImplThread impl; RefPtr<LayerChromium> layerTreeRoot = LayerChromium::create(); - layerTreeRoot->setLayerAnimationController(FakeLayerAnimationController::create()); + FakeLayerAnimationControllerClient dummy; + layerTreeRoot->setLayerAnimationController(FakeLayerAnimationController::create(&dummy)); EXPECT_FALSE(static_cast<FakeLayerAnimationController*>(layerTreeRoot->layerAnimationController())->synchronizedAnimations()); diff --git a/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp b/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp index 459255e02..92f006184 100644 --- a/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp +++ b/Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp @@ -30,115 +30,71 @@ #include "WebCompositor.h" #include "WebCompositorInputHandlerClient.h" #include "WebInputEvent.h" +#include "cc/CCActiveGestureAnimation.h" #include "cc/CCInputHandler.h" #include "cc/CCSingleThreadProxy.h" +#include "platform/WebFloatPoint.h" +#include "platform/WebPoint.h" +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <wtf/OwnPtr.h> -using WebKit::WebCompositorInputHandler; -using WebKit::WebCompositorInputHandlerImpl; +using namespace WebKit; namespace { -class MockInputHandlerClient : public WebCore::CCInputHandlerClient { - WTF_MAKE_NONCOPYABLE(MockInputHandlerClient); +class MockCCInputHandlerClient : public WebCore::CCInputHandlerClient { + WTF_MAKE_NONCOPYABLE(MockCCInputHandlerClient); public: - MockInputHandlerClient() - : m_scrollStatus(ScrollStarted) - , m_pinchStarted(false) - , m_pinchEnded(false) - , m_pinchMagnification(0) + MockCCInputHandlerClient() { } - virtual ~MockInputHandlerClient() { } + virtual ~MockCCInputHandlerClient() { } - void setScrollStatus(ScrollStatus status) { m_scrollStatus = status; } - bool pinchStarted() const { return m_pinchStarted; } - bool pinchEnded() const { return m_pinchEnded; } - float pinchMaginifcation() const { return m_pinchMagnification; } + MOCK_METHOD0(pinchGestureBegin, void()); + MOCK_METHOD2(pinchGestureUpdate, void(float magnifyDelta, const WebCore::IntPoint& anchor)); + MOCK_METHOD0(pinchGestureEnd, void()); - void resetPinch() - { - m_pinchStarted = m_pinchEnded = false; - m_pinchMagnification = 0; - } + MOCK_METHOD0(scheduleAnimation, void()); -private: - virtual void setNeedsRedraw() OVERRIDE { } - virtual ScrollStatus scrollBegin(const WebCore::IntPoint&, WebCore::CCInputHandlerClient::ScrollInputType) OVERRIDE - { - return m_scrollStatus; - } - virtual void scrollBy(const WebCore::IntSize&) OVERRIDE { } - virtual void scrollEnd() OVERRIDE { } + MOCK_METHOD2(scrollBegin, ScrollStatus(const WebCore::IntPoint&, WebCore::CCInputHandlerClient::ScrollInputType)); + MOCK_METHOD1(scrollBy, void(const WebCore::IntSize&)); + MOCK_METHOD0(scrollEnd, void()); - virtual void pinchGestureBegin() OVERRIDE - { - m_pinchStarted = true; - } - virtual void pinchGestureUpdate(float magnifyDelta, const WebCore::IntPoint& anchor) OVERRIDE - { - m_pinchMagnification = magnifyDelta; - } - virtual void pinchGestureEnd() OVERRIDE - { - m_pinchEnded = true; - } +private: virtual void startPageScaleAnimation(const WebCore::IntSize& targetPosition, bool anchorPoint, float pageScale, double startTimeMs, double durationMs) OVERRIDE { } - ScrollStatus m_scrollStatus; - bool m_pinchStarted; - bool m_pinchEnded; - float m_pinchMagnification; + virtual WebCore::CCActiveGestureAnimation* activeGestureAnimation() OVERRIDE { return 0; } + virtual void setActiveGestureAnimation(PassOwnPtr<WebCore::CCActiveGestureAnimation>) OVERRIDE { } }; -class MockWebCompositorInputHandlerClient : public WebKit::WebCompositorInputHandlerClient { +class MockWebCompositorInputHandlerClient : public WebCompositorInputHandlerClient { WTF_MAKE_NONCOPYABLE(MockWebCompositorInputHandlerClient); public: MockWebCompositorInputHandlerClient() - : m_handled(false) - , m_sendToWidget(false) + : WebCompositorInputHandlerClient() { } virtual ~MockWebCompositorInputHandlerClient() { } - void reset() - { - m_handled = false; - m_sendToWidget = false; - } - - bool handled() const { return m_handled; } - bool sendToWidget() const { return m_sendToWidget; } + MOCK_METHOD0(willShutdown, void()); + MOCK_METHOD0(didHandleInputEvent, void()); + MOCK_METHOD1(didNotHandleInputEvent, void(bool sendToWidget)); -private: - virtual void willShutdown() OVERRIDE { } - virtual void didHandleInputEvent() OVERRIDE - { - m_handled = true; - } - virtual void didNotHandleInputEvent(bool sendToWidget) OVERRIDE - { - m_sendToWidget = sendToWidget; - } + MOCK_METHOD1(transferActiveWheelFlingAnimation, void(const WebActiveWheelFlingParameters&)); - bool m_handled; - bool m_sendToWidget; }; TEST(WebCompositorInputHandlerImpl, fromIdentifier) { - WebKit::WebCompositor::initialize(0); -#ifndef NDEBUG - // WebCompositorInputHandler APIs can only be called from the compositor thread. + WebCompositor::initialize(0); WebCore::DebugScopedSetImplThread alwaysImplThread; -#endif // Before creating any WebCompositorInputHandlers, lookups for any value should fail and not crash. EXPECT_EQ(0, WebCompositorInputHandler::fromIdentifier(2)); @@ -158,126 +114,452 @@ TEST(WebCompositorInputHandlerImpl, fromIdentifier) // After the compositor is destroyed, its entry should be removed from the map. EXPECT_EQ(0, WebCompositorInputHandler::fromIdentifier(compositorIdentifier)); - - WebKit::WebCompositor::shutdown(); + WebCompositor::shutdown(); } -TEST(WebCompositorInputHandlerImpl, gestureScroll) +class WebCompositorInputHandlerImplTest : public testing::Test { +public: + WebCompositorInputHandlerImplTest() + : m_expectedDisposition(DidHandle) + { + WebCompositor::initialize(0); + m_inputHandler = WebCompositorInputHandlerImpl::create(&m_mockCCInputHandlerClient); + m_inputHandler->setClient(&m_mockClient); + } + + ~WebCompositorInputHandlerImplTest() + { + m_inputHandler->setClient(0); + m_inputHandler.clear(); + WebCompositor::shutdown(); + } + + // This is defined as a macro because when an expectation is not satisfied the only output you get + // out of gmock is the line number that set the expectation. +#define VERIFY_AND_RESET_MOCKS() do \ + { \ + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); \ + testing::Mock::VerifyAndClearExpectations(&m_mockClient); \ + switch (m_expectedDisposition) { \ + case DidHandle: \ + /* If we expect to handle events, we shouldn't get any didNotHandleInputEvent() calls with any parameter. */ \ + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0); \ + EXPECT_CALL(m_mockClient, didHandleInputEvent()); \ + break; \ + case DidNotHandle: \ + /* If we aren't expecting to handle events, we shouldn't call didHandleInputEvent(). */ \ + EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0); \ + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false)).Times(0); \ + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)); \ + break; \ + case DropEvent: \ + /* If we're expecting to drop, we shouldn't get any didHandle..() or didNotHandleInputEvent(true) calls. */ \ + EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0); \ + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)).Times(0); \ + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false)); \ + break; \ + } \ + } while (0) + +protected: + MockCCInputHandlerClient m_mockCCInputHandlerClient; + OwnPtr<WebCompositorInputHandlerImpl> m_inputHandler; + MockWebCompositorInputHandlerClient m_mockClient; + WebGestureEvent gesture; + + enum ExpectedDisposition { DidHandle, DidNotHandle, DropEvent }; + ExpectedDisposition m_expectedDisposition; + +private: + WebCore::DebugScopedSetImplThread m_alwaysImplThread; +}; + + +TEST_F(WebCompositorInputHandlerImplTest, gestureScrollStarted) { - WebKit::WebCompositor::initialize(0); -#ifndef NDEBUG - // WebCompositorInputHandler APIs can only be called from the compositor thread. - WebCore::DebugScopedSetImplThread alwaysImplThread; -#endif + // We shouldn't send any events to the widget for this gesture. + m_expectedDisposition = DidHandle; + VERIFY_AND_RESET_MOCKS(); - MockInputHandlerClient mockInputHandler; - OwnPtr<WebCompositorInputHandlerImpl> inputHandler = WebCompositorInputHandlerImpl::create(&mockInputHandler); - MockWebCompositorInputHandlerClient mockClient; - inputHandler->setClient(&mockClient); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); - WebKit::WebGestureEvent gesture; + gesture.type = WebInputEvent::GestureScrollBegin; + m_inputHandler->handleInputEvent(gesture); - gesture.type = WebKit::WebInputEvent::GestureScrollBegin; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - mockClient.reset(); + VERIFY_AND_RESET_MOCKS(); - gesture.type = WebKit::WebInputEvent::GestureScrollUpdate; - gesture.deltaY = 40; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - mockClient.reset(); + gesture.type = WebInputEvent::GestureScrollUpdate; + gesture.deltaY = -40; // -Y means scroll down - i.e. in the +Y direction. + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::height, testing::Gt(0)))); + m_inputHandler->handleInputEvent(gesture); - gesture.type = WebKit::WebInputEvent::GestureScrollEnd; + VERIFY_AND_RESET_MOCKS(); + + gesture.type = WebInputEvent::GestureScrollEnd; gesture.deltaY = 0; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - mockClient.reset(); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()); + m_inputHandler->handleInputEvent(gesture); +} - mockInputHandler.setScrollStatus(WebCore::CCInputHandlerClient::ScrollFailed); +TEST_F(WebCompositorInputHandlerImplTest, gestureScrollFailed) +{ + // We should send all events to the widget for this gesture. + m_expectedDisposition = DidNotHandle; + VERIFY_AND_RESET_MOCKS(); - gesture.type = WebKit::WebInputEvent::GestureScrollBegin; - inputHandler->handleInputEvent(gesture); - EXPECT_FALSE(mockClient.handled()); - EXPECT_TRUE(mockClient.sendToWidget()); - mockClient.reset(); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(::testing::_, ::testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); - gesture.type = WebKit::WebInputEvent::GestureScrollUpdate; - gesture.deltaY = 40; - inputHandler->handleInputEvent(gesture); - EXPECT_FALSE(mockClient.handled()); - EXPECT_TRUE(mockClient.sendToWidget()); - mockClient.reset(); + gesture.type = WebInputEvent::GestureScrollBegin; + m_inputHandler->handleInputEvent(gesture); - gesture.type = WebKit::WebInputEvent::GestureScrollEnd; - gesture.deltaY = 0; - inputHandler->handleInputEvent(gesture); - EXPECT_FALSE(mockClient.handled()); - EXPECT_TRUE(mockClient.sendToWidget()); - mockClient.reset(); + VERIFY_AND_RESET_MOCKS(); + + gesture.type = WebInputEvent::GestureScrollUpdate; + gesture.deltaY = 40; + m_inputHandler->handleInputEvent(gesture); - inputHandler->setClient(0); + VERIFY_AND_RESET_MOCKS(); - WebKit::WebCompositor::shutdown(); + gesture.type = WebInputEvent::GestureScrollEnd; + gesture.deltaY = 0; + m_inputHandler->handleInputEvent(gesture); } -TEST(WebCompositorInputHandlerImpl, gesturePinch) +TEST_F(WebCompositorInputHandlerImplTest, gestureScrollIgnored) { - WebKit::WebCompositor::initialize(0); -#ifndef NDEBUG - // WebCompositorInputHandler APIs can only be called from the compositor thread. - WebCore::DebugScopedSetImplThread alwaysImplThread; -#endif + // We shouldn't handle the GestureScrollBegin. + // Instead, we should get one didNotHandleInputEvent(false) call per handleInputEvent(), + // indicating that we could determine that there's nothing that could scroll or otherwise + // react to this gesture sequence and thus we should drop the whole gesture sequence on the floor. + m_expectedDisposition = DropEvent; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollIgnored)); + + gesture.type = WebInputEvent::GestureScrollBegin; + m_inputHandler->handleInputEvent(gesture); +} - MockInputHandlerClient mockInputHandler; - OwnPtr<WebCompositorInputHandlerImpl> inputHandler = WebCompositorInputHandlerImpl::create(&mockInputHandler); - MockWebCompositorInputHandlerClient mockClient; - inputHandler->setClient(&mockClient); +TEST_F(WebCompositorInputHandlerImplTest, gesturePinch) +{ + // We shouldn't send any events to the widget for this gesture. + m_expectedDisposition = DidHandle; + VERIFY_AND_RESET_MOCKS(); - WebKit::WebGestureEvent gesture; + gesture.type = WebInputEvent::GesturePinchBegin; + EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureBegin()); + m_inputHandler->handleInputEvent(gesture); - gesture.type = WebKit::WebInputEvent::GesturePinchBegin; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - EXPECT_TRUE(mockInputHandler.pinchStarted()); - mockClient.reset(); - mockInputHandler.resetPinch(); + VERIFY_AND_RESET_MOCKS(); - gesture.type = WebKit::WebInputEvent::GesturePinchUpdate; + gesture.type = WebInputEvent::GesturePinchUpdate; gesture.deltaX = 1.5; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - EXPECT_FALSE(mockInputHandler.pinchEnded()); - EXPECT_EQ(1.5, mockInputHandler.pinchMaginifcation()); - mockClient.reset(); - mockInputHandler.resetPinch(); - - gesture.type = WebKit::WebInputEvent::GesturePinchUpdate; + gesture.x = 7; + gesture.y = 13; + EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureUpdate(1.5, WebCore::IntPoint(7, 13))); + m_inputHandler->handleInputEvent(gesture); + + VERIFY_AND_RESET_MOCKS(); + + gesture.type = WebInputEvent::GesturePinchUpdate; gesture.deltaX = 0.5; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - EXPECT_FALSE(mockInputHandler.pinchEnded()); - EXPECT_EQ(0.5, mockInputHandler.pinchMaginifcation()); - mockClient.reset(); - mockInputHandler.resetPinch(); - - gesture.type = WebKit::WebInputEvent::GesturePinchEnd; - inputHandler->handleInputEvent(gesture); - EXPECT_TRUE(mockClient.handled()); - EXPECT_FALSE(mockClient.sendToWidget()); - EXPECT_TRUE(mockInputHandler.pinchEnded()); - mockClient.reset(); - mockInputHandler.resetPinch(); - - inputHandler->setClient(0); - - WebKit::WebCompositor::shutdown(); + gesture.x = 9; + gesture.y = 6; + EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureUpdate(.5, WebCore::IntPoint(9, 6))); + m_inputHandler->handleInputEvent(gesture); + + VERIFY_AND_RESET_MOCKS(); + + gesture.type = WebInputEvent::GesturePinchEnd; + EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureEnd()); + m_inputHandler->handleInputEvent(gesture); +} + +TEST_F(WebCompositorInputHandlerImplTest, gestureFlingStarted) +{ + // We shouldn't send any events to the widget for this gesture. + m_expectedDisposition = DidHandle; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + + gesture.type = WebInputEvent::GestureFlingStart; + gesture.deltaX = 10; + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + m_inputHandler->handleInputEvent(gesture); + + VERIFY_AND_RESET_MOCKS(); + + // Verify that a GestureFlingCancel during an animation cancels it. + gesture.type = WebInputEvent::GestureFlingCancel; + m_inputHandler->handleInputEvent(gesture); +} + +TEST_F(WebCompositorInputHandlerImplTest, gestureFlingFailed) +{ + // We should send all events to the widget for this gesture. + m_expectedDisposition = DidNotHandle; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + + gesture.type = WebInputEvent::GestureFlingStart; + m_inputHandler->handleInputEvent(gesture); + + VERIFY_AND_RESET_MOCKS(); + + // Even if we didn't start a fling ourselves, we still need to send the cancel event to the widget. + gesture.type = WebInputEvent::GestureFlingCancel; + m_inputHandler->handleInputEvent(gesture); +} + +TEST_F(WebCompositorInputHandlerImplTest, gestureFlingIgnored) +{ + m_expectedDisposition = DidNotHandle; + VERIFY_AND_RESET_MOCKS(); + + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollIgnored)); + + gesture.type = WebInputEvent::GestureFlingStart; + m_inputHandler->handleInputEvent(gesture); + + VERIFY_AND_RESET_MOCKS(); + + // Even if we didn't start a fling ourselves, we still need to send the cancel event to the widget. + gesture.type = WebInputEvent::GestureFlingCancel; + m_inputHandler->handleInputEvent(gesture); +} + +TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimates) +{ + // We shouldn't send any events to the widget for this gesture. + m_expectedDisposition = DidHandle; + VERIFY_AND_RESET_MOCKS(); + + // On the fling start, we should schedule an animation but not actually start + // scrolling. + gesture.type = WebInputEvent::GestureFlingStart; + WebFloatPoint flingDelta = WebFloatPoint(1000, 0); + WebPoint flingPoint = WebPoint(7, 13); + WebPoint flingGlobalPoint = WebPoint(17, 23); + int modifiers = 7; + gesture.deltaX = flingDelta.x; + gesture.deltaY = flingDelta.y; + gesture.x = flingPoint.x; + gesture.y = flingPoint.y; + gesture.globalX = flingGlobalPoint.x; + gesture.globalY = flingGlobalPoint.y; + gesture.modifiers = modifiers; + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + m_inputHandler->handleInputEvent(gesture); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + // The first animate call should let us pick up an animation start time, but we + // shouldn't actually move anywhere just yet. The first frame after the fling start + // will typically include the last scroll from the gesture that lead to the scroll + // (either wheel or gesture scroll), so there should be no visible hitch. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); + m_inputHandler->animate(10); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // The second call should start scrolling in the -X direction. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::width, testing::Lt(0)))); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()); + m_inputHandler->animate(10.1); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Let's say on the third call we hit a non-scrollable region. We should abort the fling and not scroll. + // We also should pass the current fling parameters out to the client so the rest of the fling can be + // transferred to the main thread. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0); + + // Expected wheel fling animation parameters: + // *) flingDelta and flingPoint should match the original GestureFlingStart event + // *) startTime should be 10 to match the time parameter of the first animate() call after the GestureFlingStart + // *) cumulativeScroll depends on the curve, but since we've animated in the -X direction the X value should be < 0 + EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf( + testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)), + testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)), + testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)), + testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)), + testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(10)), + testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, + testing::Field(&WebSize::width, testing::Gt(0)))))); + m_inputHandler->animate(10.2); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + testing::Mock::VerifyAndClearExpectations(&m_mockClient); + + // Since we've aborted the fling, the next animation should be a no-op and should not result in another + // frame being requested. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()).Times(0); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); + m_inputHandler->animate(10.3); + + // Since we've transferred the fling to the main thread, we need to pass the next GestureFlingCancel to the main + // thread as well. + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)); + gesture.type = WebInputEvent::GestureFlingCancel; + m_inputHandler->handleInputEvent(gesture); +} + +TEST_F(WebCompositorInputHandlerImplTest, gestureFlingTransferResets) +{ + // We shouldn't send any events to the widget for this gesture. + m_expectedDisposition = DidHandle; + VERIFY_AND_RESET_MOCKS(); + + // Start a gesture fling in the -X direction with zero Y movement. + gesture.type = WebInputEvent::GestureFlingStart; + WebFloatPoint flingDelta = WebFloatPoint(1000, 0); + WebPoint flingPoint = WebPoint(7, 13); + WebPoint flingGlobalPoint = WebPoint(17, 23); + int modifiers = 1; + gesture.deltaX = flingDelta.x; + gesture.deltaY = flingDelta.y; + gesture.x = flingPoint.x; + gesture.y = flingPoint.y; + gesture.globalX = flingGlobalPoint.x; + gesture.globalY = flingGlobalPoint.y; + gesture.modifiers = modifiers; + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + m_inputHandler->handleInputEvent(gesture); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Start the fling animation at time 10. This shouldn't actually scroll, just establish a start time. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); + m_inputHandler->animate(10); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // The second call should start scrolling in the -X direction. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::width, testing::Lt(0)))); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()); + m_inputHandler->animate(10.1); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Let's say on the third call we hit a non-scrollable region. We should abort the fling and not scroll. + // We also should pass the current fling parameters out to the client so the rest of the fling can be + // transferred to the main thread. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0); + + // Expected wheel fling animation parameters: + // *) flingDelta and flingPoint should match the original GestureFlingStart event + // *) startTime should be 10 to match the time parameter of the first animate() call after the GestureFlingStart + // *) cumulativeScroll depends on the curve, but since we've animated in the -X direction the X value should be < 0 + EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf( + testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)), + testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)), + testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)), + testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)), + testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(10)), + testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, + testing::Field(&WebSize::width, testing::Gt(0)))))); + m_inputHandler->animate(10.2); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + testing::Mock::VerifyAndClearExpectations(&m_mockClient); + + // Since we've aborted the fling, the next animation should be a no-op and should not result in another + // frame being requested. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()).Times(0); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); + m_inputHandler->animate(10.3); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Since we've transferred the fling to the main thread, we need to pass the next GestureFlingCancel to the main + // thread as well. + EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)); + gesture.type = WebInputEvent::GestureFlingCancel; + m_inputHandler->handleInputEvent(gesture); + + VERIFY_AND_RESET_MOCKS(); + + // Start a second gesture fling, this time in the +Y direction with no X. + gesture.type = WebInputEvent::GestureFlingStart; + flingDelta = WebFloatPoint(0, -1000); + flingPoint = WebPoint(95, 87); + flingGlobalPoint = WebPoint(32, 71); + modifiers = 2; + gesture.deltaX = flingDelta.x; + gesture.deltaY = flingDelta.y; + gesture.x = flingPoint.x; + gesture.y = flingPoint.y; + gesture.globalX = flingGlobalPoint.x; + gesture.globalY = flingGlobalPoint.y; + gesture.modifiers = modifiers; + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + m_inputHandler->handleInputEvent(gesture); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Start the second fling animation at time 30. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0); + m_inputHandler->animate(30); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Tick the second fling once normally. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted)); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::height, testing::Gt(0)))); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()); + m_inputHandler->animate(30.1); + + testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient); + + // Then abort the second fling. + EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed)); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0); + EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0); + + // We should get parameters from the second fling, nothing from the first fling should "leak". + EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf( + testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)), + testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)), + testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)), + testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)), + testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(30)), + testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll, + testing::Field(&WebSize::height, testing::Lt(0)))))); + m_inputHandler->animate(30.2); } } diff --git a/Source/WebKit/chromium/tests/WebExternalTextureLayerImplTest.cpp b/Source/WebKit/chromium/tests/WebExternalTextureLayerImplTest.cpp deleted file mode 100644 index 583d0df70..000000000 --- a/Source/WebKit/chromium/tests/WebExternalTextureLayerImplTest.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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" - -#include "WebExternalTextureLayerImpl.h" - -#include <gtest/gtest.h> -#include <wtf/RefPtr.h> - -namespace { - -using namespace WebKit; -using WTF::RefPtr; - -class WebExternalTextureLayerImplTest : public WebExternalTextureLayerImpl { -public: - bool isDrawable() const { return drawsContent(); } -}; - -TEST(WebExternalTextureLayerImpl, testIsDrawable) -{ - RefPtr<WebExternalTextureLayerImplTest> textureLayer = adoptRef(new WebExternalTextureLayerImplTest()); - - textureLayer->setTextureId(1); - EXPECT_TRUE(textureLayer->isDrawable()); -} - -} diff --git a/Source/WebKit/chromium/tests/WebFrameTest.cpp b/Source/WebKit/chromium/tests/WebFrameTest.cpp index 481bd916f..b8bc2e3c4 100644 --- a/Source/WebKit/chromium/tests/WebFrameTest.cpp +++ b/Source/WebKit/chromium/tests/WebFrameTest.cpp @@ -42,6 +42,7 @@ #include "WebSearchableFormData.h" #include "WebSecurityPolicy.h" #include "WebSettings.h" +#include "WebViewClient.h" #include "WebViewImpl.h" #include "v8.h" #include <gtest/gtest.h> @@ -151,8 +152,42 @@ TEST_F(WebFrameTest, ChromePageNoJavascript) EXPECT_EQ(std::string::npos, content.find("Clobbered")); } +#if ENABLE(VIEWPORT) + +class FixedLayoutTestWebViewClient : public WebViewClient { + public: + virtual WebRect windowRect() OVERRIDE { return m_windowRect; } + virtual WebScreenInfo screenInfo() OVERRIDE { return m_screenInfo; } + + WebRect m_windowRect; + WebScreenInfo m_screenInfo; +}; + +TEST_F(WebFrameTest, DeviceScaleFactorUsesDefaultWithoutViewportTag) +{ + registerMockedHttpURLLoad("no_viewport_tag.html"); + + int viewportWidth = 640; + int viewportHeight = 480; + + FixedLayoutTestWebViewClient client; + client.m_screenInfo.horizontalDPI = 160; + client.m_windowRect = WebRect(0, 0, viewportWidth, viewportHeight); + + WebView* webView = static_cast<WebView*>(FrameTestHelpers::createWebViewAndLoad(m_baseURL + "no_viewport_tag.html", true, 0, &client)); + + webView->resize(WebSize(viewportWidth, viewportHeight)); + webView->settings()->setViewportEnabled(true); + webView->settings()->setDefaultDeviceScaleFactor(2); + webView->enableFixedLayoutMode(true); + webView->layout(); + + EXPECT_EQ(2, webView->deviceScaleFactor()); +} +#endif + #if ENABLE(GESTURE_EVENTS) -TEST_F(WebFrameTest, DivAutoZoomParamsTest) +TEST_F(WebFrameTest, FAILS_DivAutoZoomParamsTest) { registerMockedHttpURLLoad("get_scale_for_auto_zoom_into_div_test.html"); @@ -238,7 +273,12 @@ public: { // Return a dummy error so the DocumentLoader doesn't assert when // the reload cancels it. - return WebURLError(WebCore::ResourceError("", 1, "", "cancelled")); + WebURLError webURLError; + webURLError.domain = ""; + webURLError.reason = 1; + webURLError.isCancellation = true; + webURLError.unreachableURL = WebURL(); + return webURLError; } }; diff --git a/Source/WebKit/chromium/tests/WebLayerTest.cpp b/Source/WebKit/chromium/tests/WebLayerTest.cpp index 1eed81aea..7c0831f90 100644 --- a/Source/WebKit/chromium/tests/WebLayerTest.cpp +++ b/Source/WebKit/chromium/tests/WebLayerTest.cpp @@ -136,7 +136,7 @@ TEST_F(WebLayerTest, Client) EXPECT_EQ(otherLayer, layer.maskLayer()); EXPECT_CALL(m_client, scheduleComposite()).Times(AtLeast(1)); - float opacity = 0.123; + float opacity = 0.123f; layer.setOpacity(opacity); Mock::VerifyAndClearExpectations(&m_client); EXPECT_EQ(opacity, layer.opacity()); @@ -160,18 +160,15 @@ TEST_F(WebLayerTest, Client) EXPECT_CALL(m_client, scheduleComposite()).Times(AtLeast(1)); textureLayer.setTextureId(3); Mock::VerifyAndClearExpectations(&m_client); - EXPECT_EQ(3u, textureLayer.textureId()); EXPECT_CALL(m_client, scheduleComposite()).Times(AtLeast(1)); textureLayer.setFlipped(true); Mock::VerifyAndClearExpectations(&m_client); - EXPECT_TRUE(textureLayer.flipped()); EXPECT_CALL(m_client, scheduleComposite()).Times(AtLeast(1)); - WebFloatRect uvRect(0.1, 0.1, 0.9, 0.9); + WebFloatRect uvRect(0.1f, 0.1f, 0.9f, 0.9f); textureLayer.setUVRect(uvRect); Mock::VerifyAndClearExpectations(&m_client); - EXPECT_TRUE(textureLayer.flipped()); // Content layer. diff --git a/Source/WebKit/chromium/tests/WebMediaPlayerClientImplTest.cpp b/Source/WebKit/chromium/tests/WebMediaPlayerClientImplTest.cpp new file mode 100644 index 000000000..56a1a6e22 --- /dev/null +++ b/Source/WebKit/chromium/tests/WebMediaPlayerClientImplTest.cpp @@ -0,0 +1,120 @@ +/* + * 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 "WebMediaPlayerClientImpl.h" + +#include <gtest/gtest.h> +#include <wtf/PassOwnPtr.h> + +using namespace WebKit; + +namespace { + +class FakeWebMediaPlayerClientImpl : public WebMediaPlayerClientImpl { +public: + static PassOwnPtr<FakeWebMediaPlayerClientImpl> create() { return adoptPtr(new FakeWebMediaPlayerClientImpl()); } + +private: + FakeWebMediaPlayerClientImpl() { } +}; + +class FakeVideoFrameProviderClient : public WebVideoFrameProvider::Client { +public: + static PassOwnPtr<FakeVideoFrameProviderClient> create(WebVideoFrameProvider* provider) + { + return adoptPtr(new FakeVideoFrameProviderClient(provider)); + } + + virtual ~FakeVideoFrameProviderClient() + { + if (m_provider) + m_provider->setVideoFrameProviderClient(0); + } + + // WebKit::WebVideoFrameProvider::Client implementation. + virtual void didReceiveFrame() { } + virtual void didUpdateMatrix(const float*) { } + virtual void stopUsingProvider() + { + m_provider = 0; + } + + WebVideoFrameProvider* provider() const { return m_provider; } + +private: + FakeVideoFrameProviderClient(WebVideoFrameProvider* provider) + : m_provider(provider) + { + m_provider->setVideoFrameProviderClient(this); + } + + WebVideoFrameProvider* m_provider; +}; + +TEST(WebMediaPlayerClientImplTest, InitialNullVideoClient) +{ + // No explict checks in this test; just make sure it doesn't crash. + OwnPtr<WebMediaPlayerClientImpl> provider(FakeWebMediaPlayerClientImpl::create()); + provider->setVideoFrameProviderClient(0); +} + +TEST(WebMediaPlayerClientImplTest, SetAndUnsetVideoClient) +{ + OwnPtr<WebMediaPlayerClientImpl> provider(FakeWebMediaPlayerClientImpl::create()); + OwnPtr<FakeVideoFrameProviderClient> client(FakeVideoFrameProviderClient::create(provider.get())); + + EXPECT_EQ(client->provider(), provider.get()); + + provider->setVideoFrameProviderClient(0); + ASSERT_FALSE(client->provider()); +} + +TEST(WebMediaPlayerClientImplTest, DestroyProvider) +{ + OwnPtr<WebMediaPlayerClientImpl> provider(FakeWebMediaPlayerClientImpl::create()); + OwnPtr<FakeVideoFrameProviderClient> client(FakeVideoFrameProviderClient::create(provider.get())); + + EXPECT_EQ(client->provider(), provider); + provider.clear(); + ASSERT_FALSE(client->provider()); +} + +TEST(WebMediaPlayerClientImplTest, SetMultipleVideoClients) +{ + OwnPtr<WebMediaPlayerClientImpl> provider(FakeWebMediaPlayerClientImpl::create()); + OwnPtr<FakeVideoFrameProviderClient> firstClient(FakeVideoFrameProviderClient::create(provider.get())); + EXPECT_EQ(firstClient->provider(), provider); + + OwnPtr<FakeVideoFrameProviderClient> secondClient(FakeVideoFrameProviderClient::create(provider.get())); + EXPECT_FALSE(firstClient->provider()); + EXPECT_EQ(secondClient->provider(), provider); + + provider.clear(); + ASSERT_FALSE(firstClient->provider()); + ASSERT_FALSE(secondClient->provider()); +} + +} diff --git a/Source/WebKit/chromium/tests/WebSocketDeflaterTest.cpp b/Source/WebKit/chromium/tests/WebSocketDeflaterTest.cpp index 423a5969f..1d7826a69 100644 --- a/Source/WebKit/chromium/tests/WebSocketDeflaterTest.cpp +++ b/Source/WebKit/chromium/tests/WebSocketDeflaterTest.cpp @@ -46,8 +46,8 @@ TEST(WebSocketDeflaterTest, TestCompressHello) ASSERT_TRUE(deflater->addBytes(inputData, inputLength)); ASSERT_TRUE(deflater->finish()); - const char expectedFirst[] = {0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00}; - EXPECT_EQ(sizeof(expectedFirst), deflater->size()); + const char* expectedFirst = "\xf2\x48\xcd\xc9\xc9\x07\x00"; + EXPECT_EQ(7U, deflater->size()); EXPECT_EQ(0, memcmp(expectedFirst, deflater->data(), deflater->size())); ASSERT_TRUE(inflater->addBytes(deflater->data(), deflater->size())); ASSERT_TRUE(inflater->finish()); @@ -59,8 +59,8 @@ TEST(WebSocketDeflaterTest, TestCompressHello) ASSERT_TRUE(deflater->addBytes(inputData, inputLength)); ASSERT_TRUE(deflater->finish()); - const char expectedSecond[] = {0xf2, 0x00, 0x11, 0x00, 0x00}; - EXPECT_EQ(sizeof(expectedSecond), deflater->size()); + const char* expectedSecond = "\xf2\x00\x11\x00\x00"; + EXPECT_EQ(5U, deflater->size()); EXPECT_EQ(0, memcmp(expectedSecond, deflater->data(), deflater->size())); ASSERT_TRUE(inflater->addBytes(deflater->data(), deflater->size())); ASSERT_TRUE(inflater->finish()); @@ -93,7 +93,7 @@ TEST(WebSocketDeflaterTest, TestNoContextTakeOver) ASSERT_TRUE(deflater->initialize()); OwnPtr<WebSocketInflater> inflater = WebSocketInflater::create(); ASSERT_TRUE(inflater->initialize()); - const char expected[] = {0xf2, 0x48, 0xcd, 0xc9, 0xc9, 0x07, 0x00}; + const char* expected = "\xf2\x48\xcd\xc9\xc9\x07\x00"; const char* inputData = "Hello"; const size_t inputLength = strlen(inputData); @@ -102,7 +102,7 @@ TEST(WebSocketDeflaterTest, TestNoContextTakeOver) for (size_t i = 0; i < 2; ++i) { ASSERT_TRUE(deflater->addBytes(inputData, inputLength)); ASSERT_TRUE(deflater->finish()); - EXPECT_EQ(sizeof(expected), deflater->size()); + EXPECT_EQ(7U, deflater->size()); EXPECT_EQ(0, memcmp(expected, deflater->data(), deflater->size())); ASSERT_TRUE(inflater->addBytes(deflater->data(), deflater->size())); ASSERT_TRUE(inflater->finish()); diff --git a/Source/WebKit/chromium/tests/WebViewTest.cpp b/Source/WebKit/chromium/tests/WebViewTest.cpp index 6580d06eb..00d8a832d 100644 --- a/Source/WebKit/chromium/tests/WebViewTest.cpp +++ b/Source/WebKit/chromium/tests/WebViewTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011, 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 @@ -42,6 +42,7 @@ #include "platform/WebSize.h" #include "WebViewClient.h" #include "WebViewImpl.h" +#include <googleurl/src/gurl.h> #include <gtest/gtest.h> #include <webkit/support/webkit_support.h> @@ -49,12 +50,28 @@ using namespace WebKit; namespace { +enum HorizontalScrollbarState { + NoHorizontalScrollbar, + VisibleHorizontalScrollbar, +}; + +enum VerticalScrollbarState { + NoVerticalScrollbar, + VisibleVerticalScrollbar, +}; + class TestData { public: void setWebView(WebView* webView) { m_webView = static_cast<WebViewImpl*>(webView); } void setSize(const WebSize& newSize) { m_size = newSize; } - bool hasHorizontalScrollbar() const { return m_webView->hasHorizontalScrollbar(); } - bool hasVerticalScrollbar() const { return m_webView->hasVerticalScrollbar(); } + HorizontalScrollbarState horizontalScrollbarState() const + { + return m_webView->hasHorizontalScrollbar() ? VisibleHorizontalScrollbar: NoHorizontalScrollbar; + } + VerticalScrollbarState verticalScrollbarState() const + { + return m_webView->hasVerticalScrollbar() ? VisibleVerticalScrollbar : NoVerticalScrollbar; + } int width() const { return m_size.width; } int height() const { return m_size.height; } @@ -88,6 +105,11 @@ public: } protected: + void testAutoResize(const WebSize& minAutoResize, const WebSize& maxAutoResize, + const std::string& pageWidth, const std::string& pageHeight, + int expectedWidth, int expectedHeight, + HorizontalScrollbarState expectedHorizontalState, VerticalScrollbarState expectedVerticalState); + std::string m_baseURL; }; @@ -119,35 +141,108 @@ TEST_F(WebViewTest, FocusIsInactive) webView->close(); } -TEST_F(WebViewTest, AutoResizeMinimumSize) +void WebViewTest::testAutoResize(const WebSize& minAutoResize, const WebSize& maxAutoResize, + const std::string& pageWidth, const std::string& pageHeight, + int expectedWidth, int expectedHeight, + HorizontalScrollbarState expectedHorizontalState, VerticalScrollbarState expectedVerticalState) { AutoResizeWebViewClient client; - FrameTestHelpers::registerMockedURLLoad(m_baseURL, "specify_size.html"); - WebView* webView = FrameTestHelpers::createWebViewAndLoad(m_baseURL + "specify_size.html", true, 0, &client); + std::string url = m_baseURL + "specify_size.html?" + pageWidth + ":" + pageHeight; + FrameTestHelpers::registerMockedURLLoad(GURL(url), "specify_size.html"); + WebView* webView = FrameTestHelpers::createWebViewAndLoad(url, true, 0, &client); client.testData().setWebView(webView); - FrameTestHelpers::loadFrame(webView->mainFrame(), "javascript:document.getElementById('sizer').style.height = '56px';"); - FrameTestHelpers::loadFrame(webView->mainFrame(), "javascript:document.getElementById('sizer').style.width = '91px';"); WebFrameImpl* frame = static_cast<WebFrameImpl*>(webView->mainFrame()); WebCore::FrameView* frameView = frame->frame()->view(); + frameView->layout(); EXPECT_FALSE(frameView->layoutPending()); EXPECT_FALSE(frameView->needsLayout()); - WebSize minSize(91, 56); - WebSize maxSize(403, 302); - webView->enableAutoResizeMode(true, minSize, maxSize); + webView->enableAutoResizeMode(minAutoResize, maxAutoResize); EXPECT_TRUE(frameView->layoutPending()); EXPECT_TRUE(frameView->needsLayout()); frameView->layout(); EXPECT_TRUE(frame->frame()->document()->isHTMLDocument()); - EXPECT_EQ(91, client.testData().width()); - EXPECT_EQ(56, client.testData().height()); - EXPECT_FALSE(client.testData().hasHorizontalScrollbar()); - EXPECT_FALSE(client.testData().hasVerticalScrollbar()); + EXPECT_EQ(expectedWidth, client.testData().width()); + EXPECT_EQ(expectedHeight, client.testData().height()); + EXPECT_EQ(expectedHorizontalState, client.testData().horizontalScrollbarState()); + EXPECT_EQ(expectedVerticalState, client.testData().verticalScrollbarState()); webView->close(); } +TEST_F(WebViewTest, AutoResizeMinimumSize) +{ + WebSize minAutoResize(91, 56); + WebSize maxAutoResize(403, 302); + std::string pageWidth = "91px"; + std::string pageHeight = "56px"; + int expectedWidth = 91; + int expectedHeight = 56; + testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, + expectedWidth, expectedHeight, NoHorizontalScrollbar, NoVerticalScrollbar); +} + +TEST_F(WebViewTest, AutoResizeHeightOverflowAndFixedWidth) +{ + WebSize minAutoResize(90, 95); + WebSize maxAutoResize(90, 100); + std::string pageWidth = "60px"; + std::string pageHeight = "200px"; + int expectedWidth = 90; + int expectedHeight = 100; + testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, + expectedWidth, expectedHeight, NoHorizontalScrollbar, VisibleVerticalScrollbar); +} + +TEST_F(WebViewTest, AutoResizeFixedHeightAndWidthOverflow) +{ + WebSize minAutoResize(90, 100); + WebSize maxAutoResize(200, 100); + std::string pageWidth = "300px"; + std::string pageHeight = "80px"; + int expectedWidth = 200; + int expectedHeight = 100; + testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, + expectedWidth, expectedHeight, VisibleHorizontalScrollbar, NoVerticalScrollbar); +} + +TEST_F(WebViewTest, AutoResizeInBetweenSizes) +{ + WebSize minAutoResize(90, 95); + WebSize maxAutoResize(200, 300); + std::string pageWidth = "100px"; + std::string pageHeight = "200px"; + int expectedWidth = 100; + int expectedHeight = 200; + testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, + expectedWidth, expectedHeight, NoHorizontalScrollbar, NoVerticalScrollbar); +} + +TEST_F(WebViewTest, AutoResizeOverflowSizes) +{ + WebSize minAutoResize(90, 95); + WebSize maxAutoResize(200, 300); + std::string pageWidth = "300px"; + std::string pageHeight = "400px"; + int expectedWidth = 200; + int expectedHeight = 300; + testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, + expectedWidth, expectedHeight, VisibleHorizontalScrollbar, VisibleVerticalScrollbar); +} + +TEST_F(WebViewTest, AutoResizeMaxSize) +{ + WebSize minAutoResize(90, 95); + WebSize maxAutoResize(200, 300); + std::string pageWidth = "200px"; + std::string pageHeight = "300px"; + int expectedWidth = 200; + int expectedHeight = 300; + testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, + expectedWidth, expectedHeight, NoHorizontalScrollbar, NoVerticalScrollbar); +} + } diff --git a/Source/WebKit/chromium/tests/data/no_viewport_tag.html b/Source/WebKit/chromium/tests/data/no_viewport_tag.html new file mode 100644 index 000000000..9ba981574 --- /dev/null +++ b/Source/WebKit/chromium/tests/data/no_viewport_tag.html @@ -0,0 +1,5 @@ +<html> + <body> + <p> Test </p> + </body> +</html> diff --git a/Source/WebKit/chromium/tests/data/specify_size.html b/Source/WebKit/chromium/tests/data/specify_size.html index 7b9695fd2..2041e85db 100644 --- a/Source/WebKit/chromium/tests/data/specify_size.html +++ b/Source/WebKit/chromium/tests/data/specify_size.html @@ -1,6 +1,20 @@ <!doctype html> <html> <body style='margin:0px'> -<img id='sizer' src='broken.jpg'/> +<img id='sizer'/> +<script> +// The desired size should be specified in the url in the search portion +// like this ?widthDimension:heightDimension. +var unitMatch = '([0-9]+[a-zA-Z]+)'; +var searchParser = new RegExp('\\?' + unitMatch + '(?:\\:' + unitMatch + ')'); +var parsedParameters = searchParser.exec(window.location.search); + +// Set the sizes. +var elementToSize = document.getElementById('sizer'); +var width = parsedParameters[1]; +elementToSize.style.width = width; +var height = parsedParameters[2]; +elementToSize.style.height = height; +</script> </body> </html> |