[chromium] Layers with animating transforms should prepaint even if they are not...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 26 Mar 2012 06:02:56 +0000 (06:02 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 26 Mar 2012 06:02:56 +0000 (06:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=82117

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

Source/WebCore:

For animating transforms, instead of early-outing when the layer's
visible rect is empty, let it prepaint regardless.

For now, we just only paint the outermost tiles, and only for small
layers, with at most 9 tiles.

This changes the behaviour of ContentLayerChromium's
idlePaintContentsIfDirty() so I've guarded the behaviour of the two
prepainting functions that it calls to ensure the old behaviour holds
without animations, and the new behaviour works with them.

Unit test: TiledLayerChromiumTest.idlePaintZeroSizedLayer
           TiledLayerChromiumTest.idlePaintZeroSizedAnimatingLayer
           TiledLayerChromiumTest.idlePaintNonVisibleLayers
           TiledLayerChromiumTest.idlePaintNonVisibleAnimatingLayers

* platform/graphics/chromium/ContentLayerChromium.cpp:
(WebCore::ContentLayerChromium::idlePaintContentsIfDirty):
* platform/graphics/chromium/TiledLayerChromium.cpp:
(WebCore::TiledLayerChromium::prepareToUpdateIdle):
(WebCore::TiledLayerChromium::needsIdlePaint):
(WebCore::TiledLayerChromium::idlePaintRect):
* platform/graphics/chromium/TiledLayerChromium.h:
(WebCore::TiledLayerChromium::numPaintedTiles):
(TiledLayerChromium):

Source/WebKit/chromium:

* tests/TiledLayerChromiumTest.cpp:
(WTF::FakeTiledLayerChromium::FakeTiledLayerChromium):
(WTF::FakeTiledLayerChromium::tileSize):
(FakeTiledLayerChromium):
(WTF::TEST):
(WTF):
(WTF::idlePaintRepeat):
(WTF::testHaveOuterTiles):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp
Source/WebCore/platform/graphics/chromium/TiledLayerChromium.cpp
Source/WebCore/platform/graphics/chromium/TiledLayerChromium.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp

index 3fd5935..22e29c5 100644 (file)
@@ -1,3 +1,36 @@
+2012-03-25  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Layers with animating transforms should prepaint even if they are not visible yet
+        https://bugs.webkit.org/show_bug.cgi?id=82117
+
+        Reviewed by Adrienne Walker.
+
+        For animating transforms, instead of early-outing when the layer's
+        visible rect is empty, let it prepaint regardless.
+
+        For now, we just only paint the outermost tiles, and only for small
+        layers, with at most 9 tiles.
+
+        This changes the behaviour of ContentLayerChromium's
+        idlePaintContentsIfDirty() so I've guarded the behaviour of the two
+        prepainting functions that it calls to ensure the old behaviour holds
+        without animations, and the new behaviour works with them.
+
+        Unit test: TiledLayerChromiumTest.idlePaintZeroSizedLayer
+                   TiledLayerChromiumTest.idlePaintZeroSizedAnimatingLayer
+                   TiledLayerChromiumTest.idlePaintNonVisibleLayers
+                   TiledLayerChromiumTest.idlePaintNonVisibleAnimatingLayers
+
+        * platform/graphics/chromium/ContentLayerChromium.cpp:
+        (WebCore::ContentLayerChromium::idlePaintContentsIfDirty):
+        * platform/graphics/chromium/TiledLayerChromium.cpp:
+        (WebCore::TiledLayerChromium::prepareToUpdateIdle):
+        (WebCore::TiledLayerChromium::needsIdlePaint):
+        (WebCore::TiledLayerChromium::idlePaintRect):
+        * platform/graphics/chromium/TiledLayerChromium.h:
+        (WebCore::TiledLayerChromium::numPaintedTiles):
+        (TiledLayerChromium):
+
 2012-03-25  Antti Koivisto  <antti@apple.com>
 
         Don't use CSSRuleList for child rule ownership
index c007215..73a8a42 100644 (file)
@@ -114,10 +114,7 @@ void ContentLayerChromium::idlePaintContentsIfDirty(const CCOcclusionTracker* oc
     if (!drawsContent())
         return;
 
-    const IntRect& layerRect = visibleLayerRect();
-    if (layerRect.isEmpty())
-        return;
-
+    const IntRect layerRect = visibleLayerRect();
     prepareToUpdateIdle(layerRect, occlusion);
     if (needsIdlePaint(layerRect))
         setNeedsCommit();
index af594d4..5b0089a 100644 (file)
@@ -628,25 +628,46 @@ void TiledLayerChromium::prepareToUpdateIdle(const IntRect& layerRect, const CCO
 
     updateBounds();
 
-    if (m_tiler->isEmpty())
+    if (!m_tiler->numTiles())
         return;
 
-    // Protect any textures in the pre-paint area so we don't end up just
-    // reclaiming them below.
     IntRect idlePaintLayerRect = idlePaintRect(layerRect);
+    if (idlePaintLayerRect.isEmpty())
+        return;
+
+    // Protect any textures in the pre-paint area, as we would steal them from other layers
+    // over time anyhow. This ensures we don't lose tiles in the first rounds of idle painting
+    // that we have already painted.
     protectTileTextures(idlePaintLayerRect);
 
+    int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
+    m_tiler->layerRectToTileIndices(idlePaintLayerRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
+
+    // If the layer is not visible, we have nothing to expand from, so instead we prepaint the outer-most set of tiles.
+    if (layerRect.isEmpty()) {
+        prepareToUpdateTiles(true, prepaintLeft, prepaintTop, prepaintRight, prepaintTop, 0);
+        if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+            return;
+        prepareToUpdateTiles(true, prepaintLeft, prepaintBottom, prepaintRight, prepaintBottom, 0);
+        if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+            return;
+        prepareToUpdateTiles(true, prepaintLeft, prepaintTop, prepaintLeft, prepaintBottom, 0);
+        if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+            return;
+        prepareToUpdateTiles(true, prepaintRight, prepaintTop, prepaintRight, prepaintBottom, 0);
+
+        return;
+    }
+
     int left, top, right, bottom;
     m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
 
-    // Prepaint anything that was occluded but inside the layer's visible region.
+    // Otherwise, prepaint anything that was occluded but inside the layer's visible region.
     prepareToUpdateTiles(true, left, top, right, bottom, 0);
     if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
         return;
 
-    // Expand outwards until we find a dirty row or column to update.
-    int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
-    m_tiler->layerRectToTileIndices(idlePaintLayerRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
+    // Then expand outwards from the visible area until we find a dirty row or column to update.
     while (!m_skipsIdlePaint && (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom)) {
         if (bottom < prepaintBottom) {
             ++bottom;
@@ -680,12 +701,20 @@ bool TiledLayerChromium::needsIdlePaint(const IntRect& layerRect)
     if (m_skipsIdlePaint)
         return false;
 
+    if (!m_tiler->numTiles())
+        return false;
+
     IntRect idlePaintLayerRect = idlePaintRect(layerRect);
+    if (idlePaintLayerRect.isEmpty())
+        return false;
 
     int left, top, right, bottom;
     m_tiler->layerRectToTileIndices(idlePaintLayerRect, left, top, right, bottom);
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
+            // If the layerRect is empty, then we are painting the outer-most set of tiles only.
+            if (layerRect.isEmpty() && i != left && i != right && j != top && j != bottom)
+                continue;
             if (m_requestedUpdateTilesRect.contains(IntPoint(i, j)))
                 continue;
             UpdatableTile* tile = tileAt(i, j);
@@ -698,6 +727,15 @@ bool TiledLayerChromium::needsIdlePaint(const IntRect& layerRect)
 
 IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleLayerRect)
 {
+    // For layers that are animating transforms but not visible at all, we don't know what part
+    // of them is going to become visible. For small layers we return the entire layer, for larger
+    // ones we avoid prepainting the layer at all.
+    if (visibleLayerRect.isEmpty()) {
+        if ((drawTransformIsAnimating() || screenSpaceTransformIsAnimating()) && m_tiler->numTiles() <= 9)
+            return IntRect(IntPoint(), contentBounds());
+        return IntRect();
+    }
+
     IntRect prepaintRect = visibleLayerRect;
     // FIXME: This can be made a lot larger if we can:
     // - reserve memory at a lower priority than for visible content
index 643f696..fd478de 100644 (file)
@@ -77,6 +77,7 @@ protected:
     void setTextureFormat(GC3Denum textureFormat) { m_textureFormat = textureFormat; }
     void setBorderTexelOption(CCLayerTilingData::BorderTexelOption);
     void setSampledTexelFormat(LayerTextureUpdater::SampledTexelFormat sampledTexelFormat) { m_sampledTexelFormat = sampledTexelFormat; }
+    size_t numPaintedTiles() { return m_tiler->tiles().size(); }
 
     virtual LayerTextureUpdater* textureUpdater() const = 0;
     virtual void createTextureUpdaterIfNeeded() = 0;
@@ -96,6 +97,8 @@ protected:
     // After preparing an update, returns true if more pre-painting is needed.
     bool needsIdlePaint(const IntRect& layerRect);
 
+    IntRect idlePaintRect(const IntRect& visibleLayerRect);
+
     bool skipsDraw() const { return m_skipsDraw; }
 
     virtual void protectVisibleTileTextures();
@@ -112,7 +115,6 @@ private:
     bool tileNeedsBufferedUpdate(UpdatableTile*);
 
     void prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom, const CCOcclusionTracker*);
-    IntRect idlePaintRect(const IntRect& visibleLayerRect);
 
     UpdatableTile* tileAt(int, int) const;
     UpdatableTile* createTile(int, int);
index 8b6515d..77d9383 100644 (file)
@@ -1,3 +1,19 @@
+2012-03-25  Dana Jansens  <danakj@chromium.org>
+
+        [chromium] Layers with animating transforms should prepaint even if they are not visible yet
+        https://bugs.webkit.org/show_bug.cgi?id=82117
+
+        Reviewed by Adrienne Walker.
+
+        * tests/TiledLayerChromiumTest.cpp:
+        (WTF::FakeTiledLayerChromium::FakeTiledLayerChromium):
+        (WTF::FakeTiledLayerChromium::tileSize):
+        (FakeTiledLayerChromium):
+        (WTF::TEST):
+        (WTF):
+        (WTF::idlePaintRepeat):
+        (WTF::testHaveOuterTiles):
+
 2012-03-24  Nat Duca  <nduca@chromium.org>
 
         [chromium] Instrument gesture animations with async traces
index 06a9a97..c19ac46 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "TiledLayerChromium.h"
 
+#include "CCAnimationTestCommon.h"
 #include "CCLayerTreeTestCommon.h"
 #include "FakeCCLayerTreeHostClient.h"
 #include "LayerTextureUpdater.h"
@@ -38,6 +39,7 @@
 #include <gtest/gtest.h>
 
 using namespace WebCore;
+using namespace WebKitTests;
 using namespace WTF;
 
 #define EXPECT_EQ_RECT(a, b) \
@@ -160,37 +162,22 @@ public:
         , m_fakeTextureUpdater(adoptRef(new FakeLayerTextureUpdater))
         , m_textureManager(textureManager)
     {
-        setTileSize(IntSize(100, 100));
+        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.
     }
     virtual ~FakeTiledLayerChromium() { }
 
-    void invalidateRect(const IntRect& rect)
-    {
-        TiledLayerChromium::invalidateRect(rect);
-    }
+    static IntSize tileSize() { return IntSize(100, 100); }
 
-    void prepareToUpdate(const IntRect& rect, const CCOcclusionTracker* occlusion)
-    {
-        TiledLayerChromium::prepareToUpdate(rect, occlusion);
-    }
-
-    void prepareToUpdateIdle(const IntRect& rect, const CCOcclusionTracker* occlusion)
-    {
-        TiledLayerChromium::prepareToUpdateIdle(rect, occlusion);
-    }
-
-    bool needsIdlePaint(const IntRect& rect)
-    {
-        return TiledLayerChromium::needsIdlePaint(rect);
-    }
-
-    bool skipsDraw() const
-    {
-        return TiledLayerChromium::skipsDraw();
-    }
+    using TiledLayerChromium::invalidateRect;
+    using TiledLayerChromium::prepareToUpdate;
+    using TiledLayerChromium::prepareToUpdateIdle;
+    using TiledLayerChromium::needsIdlePaint;
+    using TiledLayerChromium::skipsDraw;
+    using TiledLayerChromium::numPaintedTiles;
+    using TiledLayerChromium::idlePaintRect;
 
     virtual void setNeedsDisplayRect(const FloatRect& rect)
     {
@@ -687,6 +674,211 @@ TEST(TiledLayerChromiumTest, idlePaintOutOfMemory)
     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)));
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    // The layer's bounds are empty.
+    IntRect contentRect;
+
+    layer->setBounds(contentRect.size());
+    layer->setVisibleLayerRect(contentRect);
+    layer->invalidateRect(contentRect);
+    layer->prepareToUpdate(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->updateCompositorResources(0, updater);
+    layer->pushPropertiesTo(layerImpl.get());
+
+    // Empty layers don't have tiles.
+    EXPECT_FALSE(layerImpl->hasTileAt(0, 0));
+
+    // Non-visible layers don't idle paint.
+    layer->prepareToUpdateIdle(contentRect, 0);
+
+    // Empty layers don't have tiles.
+    EXPECT_EQ(0u, layer->numPaintedTiles());
+
+    layer->updateCompositorResources(0, updater);
+    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)));
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    // 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->prepareToUpdate(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->updateCompositorResources(0, updater);
+    layer->pushPropertiesTo(layerImpl.get());
+
+    // Empty layers don't have tiles.
+    EXPECT_FALSE(layerImpl->hasTileAt(0, 0));
+
+    // Non-visible layers don't idle paint.
+    layer->prepareToUpdateIdle(contentRect, 0);
+
+    // Empty layers don't have tiles.
+    EXPECT_EQ(0u, layer->numPaintedTiles());
+
+    layer->updateCompositorResources(0, updater);
+    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)));
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    // Invalidate the layer but make none of it visible, so nothing paints.
+    IntRect visibleRect;
+
+    layer->setBounds(contentBounds);
+    layer->setVisibleLayerRect(visibleRect);
+    layer->invalidateRect(contentRect);
+    layer->prepareToUpdate(visibleRect, 0);
+
+    // Non-visible layers don't need idle paint.
+    EXPECT_FALSE(layer->needsIdlePaint(visibleRect));
+
+    layer->updateCompositorResources(0, updater);
+    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->prepareToUpdateIdle(visibleRect, 0);
+
+    layer->updateCompositorResources(0, updater);
+    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->prepareToUpdate(visibleRect, 0);
+        layer->prepareToUpdateIdle(visibleRect, 0);
+        layer->updateCompositorResources(0, updater);
+        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;
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    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->prepareToUpdate(visibleRect, 0);
+            layer->updateCompositorResources(0, updater);
+            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);