[chromium] Need to prepaint tiles in TiledLayerChromium
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 17 Dec 2011 00:27:33 +0000 (00:27 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 17 Dec 2011 00:27:33 +0000 (00:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=72686

Patch by Eric Penner <epenner@google.com> on 2011-12-16
Reviewed by James Robinson.

Source/WebCore:

Tests: TiledLayerChromiumTest (idlePaintOutOfMemory, pushIdlePaintTiles)

* platform/graphics/chromium/ContentLayerChromium.cpp:
(WebCore::ContentLayerChromium::idlePaintContentsIfDirty): added idle paint function
* platform/graphics/chromium/ContentLayerChromium.h: ditto
* platform/graphics/chromium/LayerChromium.h: ditto
(WebCore::LayerChromium::idlePaintContentsIfDirty): ditto
* platform/graphics/chromium/TextureManager.cpp:
(WebCore::TextureManager::protectTexture): removed assert for protecting a texture twice
* platform/graphics/chromium/TiledLayerChromium.cpp:
(WebCore::TiledLayerChromium::TiledLayerChromium):
(WebCore::TiledLayerChromium::cleanupResources):
(WebCore::TiledLayerChromium::updateCompositorResources): refactoring to use tile indices
(WebCore::TiledLayerChromium::prepareToUpdateTiles): refactored common code and made idle/visible versions
(WebCore::TiledLayerChromium::prepareToUpdate): ditto
(WebCore::TiledLayerChromium::prepareToUpdateIdle): ditto
(WebCore::TiledLayerChromium::needsIdlePaint):
(WebCore::TiledLayerChromium::idlePaintRect):
* platform/graphics/chromium/TiledLayerChromium.h:
* platform/graphics/chromium/cc/CCLayerTreeHost.cpp:
(WebCore::CCLayerTreeHost::CCLayerTreeHost):
(WebCore::CCLayerTreeHost::compositeAndReadback): set flag to avoid idle paint durring composite and readback
(WebCore::CCLayerTreeHost::updateLayers): added idle flag parameter
(WebCore::CCLayerTreeHost::paintContentsIfDirty): ditto
(WebCore::CCLayerTreeHost::paintMaskAndReplicaForRenderSurface): ditto
(WebCore::CCLayerTreeHost::paintLayerContents): chooses idle or visible paint
* platform/graphics/chromium/cc/CCLayerTreeHost.h:

Source/WebKit/chromium:

* tests/CCLayerTreeHostTest.cpp:
(WTF::ContentLayerChromiumWithUpdateTracking::idlePaintContentsCount):
(WTF::ContentLayerChromiumWithUpdateTracking::resetPaintContentsCount):
(WTF::ContentLayerChromiumWithUpdateTracking::idlePaintContentsIfDirty):
(WTF::ContentLayerChromiumWithUpdateTracking::ContentLayerChromiumWithUpdateTracking):
(WTF::CCLayerTreeHostTestOpacityChange::afterTest):
* tests/TiledLayerChromiumTest.cpp:
(WTF::FakeTiledLayerChromium::prepareToUpdateIdle):
(WTF::FakeTiledLayerChromium::needsIdlePaint):
(WTF::TEST):

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

12 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp
Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h
Source/WebCore/platform/graphics/chromium/LayerChromium.h
Source/WebCore/platform/graphics/chromium/TextureManager.cpp
Source/WebCore/platform/graphics/chromium/TiledLayerChromium.cpp
Source/WebCore/platform/graphics/chromium/TiledLayerChromium.h
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.cpp
Source/WebCore/platform/graphics/chromium/cc/CCLayerTreeHost.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/tests/CCLayerTreeHostTest.cpp
Source/WebKit/chromium/tests/TiledLayerChromiumTest.cpp

index 937e334..19d8f6a 100644 (file)
@@ -1,3 +1,38 @@
+2011-12-16  Eric Penner  <epenner@google.com>
+
+        [chromium] Need to prepaint tiles in TiledLayerChromium
+        https://bugs.webkit.org/show_bug.cgi?id=72686
+
+        Reviewed by James Robinson.
+
+        Tests: TiledLayerChromiumTest (idlePaintOutOfMemory, pushIdlePaintTiles)
+
+        * platform/graphics/chromium/ContentLayerChromium.cpp:
+        (WebCore::ContentLayerChromium::idlePaintContentsIfDirty): added idle paint function
+        * platform/graphics/chromium/ContentLayerChromium.h: ditto
+        * platform/graphics/chromium/LayerChromium.h: ditto
+        (WebCore::LayerChromium::idlePaintContentsIfDirty): ditto
+        * platform/graphics/chromium/TextureManager.cpp:
+        (WebCore::TextureManager::protectTexture): removed assert for protecting a texture twice
+        * platform/graphics/chromium/TiledLayerChromium.cpp:
+        (WebCore::TiledLayerChromium::TiledLayerChromium):
+        (WebCore::TiledLayerChromium::cleanupResources):
+        (WebCore::TiledLayerChromium::updateCompositorResources): refactoring to use tile indices
+        (WebCore::TiledLayerChromium::prepareToUpdateTiles): refactored common code and made idle/visible versions
+        (WebCore::TiledLayerChromium::prepareToUpdate): ditto
+        (WebCore::TiledLayerChromium::prepareToUpdateIdle): ditto
+        (WebCore::TiledLayerChromium::needsIdlePaint): 
+        (WebCore::TiledLayerChromium::idlePaintRect):
+        * platform/graphics/chromium/TiledLayerChromium.h:
+        * platform/graphics/chromium/cc/CCLayerTreeHost.cpp:
+        (WebCore::CCLayerTreeHost::CCLayerTreeHost):
+        (WebCore::CCLayerTreeHost::compositeAndReadback): set flag to avoid idle paint durring composite and readback
+        (WebCore::CCLayerTreeHost::updateLayers): added idle flag parameter
+        (WebCore::CCLayerTreeHost::paintContentsIfDirty): ditto
+        (WebCore::CCLayerTreeHost::paintMaskAndReplicaForRenderSurface): ditto
+        (WebCore::CCLayerTreeHost::paintLayerContents): chooses idle or visible paint
+        * platform/graphics/chromium/cc/CCLayerTreeHost.h:
+
 2011-12-16  Dean Jackson  <dino@apple.com>
 
         Miscellaneous Filter updates to align with spec
index 78eb7c8..c639563 100644 (file)
@@ -108,6 +108,20 @@ void ContentLayerChromium::paintContentsIfDirty()
     m_needsDisplay = false;
 }
 
+void ContentLayerChromium::idlePaintContentsIfDirty()
+{
+    if (!drawsContent())
+        return;
+
+    const IntRect& layerRect = visibleLayerRect();
+    if (layerRect.isEmpty())
+        return;
+
+    prepareToUpdateIdle(layerRect);
+    if (needsIdlePaint(layerRect))
+        setNeedsCommit();
+}
+
 bool ContentLayerChromium::drawsContent() const
 {
     return m_delegate && m_delegate->drawsContent() && TiledLayerChromium::drawsContent();
index b834e67..0c574b5 100644 (file)
@@ -50,6 +50,7 @@ public:
     virtual ~ContentLayerChromium();
 
     virtual void paintContentsIfDirty();
+    virtual void idlePaintContentsIfDirty();
 
 protected:
     explicit ContentLayerChromium(CCLayerDelegate*);
index c2b2747..762a787 100644 (file)
@@ -161,6 +161,7 @@ public:
     // These methods typically need to be overwritten by derived classes.
     virtual bool drawsContent() const { return false; }
     virtual void paintContentsIfDirty() { }
+    virtual void idlePaintContentsIfDirty() { }
     virtual void updateCompositorResources(GraphicsContext3D*, CCTextureUpdater&) { }
     virtual void setIsMask(bool) { }
     virtual void unreserveContentsTexture() { }
index 1187537..30207be 100644 (file)
@@ -141,7 +141,6 @@ bool TextureManager::isProtected(TextureToken token)
 void TextureManager::protectTexture(TextureToken token)
 {
     ASSERT(hasTexture(token));
-    ASSERT(!m_textures.get(token).isProtected);
     TextureInfo info = m_textures.take(token);
     info.isProtected = true;
     m_textures.add(token, info);
index 87cba88..a0e9102 100644 (file)
@@ -72,6 +72,7 @@ TiledLayerChromium::TiledLayerChromium(CCLayerDelegate* delegate)
     : LayerChromium(delegate)
     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
     , m_skipsDraw(false)
+    , m_skipsIdlePaint(false)
     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
     , m_tilingOption(AutoTile)
 {
@@ -92,7 +93,7 @@ void TiledLayerChromium::cleanupResources()
 
     m_tiler.clear();
     m_paintRect = IntRect();
-    m_requestedUpdateRect = IntRect();
+    m_requestedUpdateTilesRect = IntRect();
 }
 
 void TiledLayerChromium::updateTileSizeAndTilingOption()
@@ -182,11 +183,13 @@ void TiledLayerChromium::updateCompositorResources(GraphicsContext3D*, CCTexture
     ASSERT(m_skipsDraw || m_tiler);
 
     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
-    if (m_skipsDraw || m_requestedUpdateRect.isEmpty() || !m_tiler || !m_tiler->numTiles())
+    if (m_skipsDraw || m_requestedUpdateTilesRect.isEmpty() || !m_tiler || !m_tiler->numTiles())
         return;
 
-    int left, top, right, bottom;
-    m_tiler->contentRectToTileIndices(m_requestedUpdateRect, left, top, right, bottom);
+    int left = m_requestedUpdateTilesRect.x();
+    int top = m_requestedUpdateTilesRect.y();
+    int right = m_requestedUpdateTilesRect.maxX() - 1;
+    int bottom = m_requestedUpdateTilesRect.maxY() - 1;
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
@@ -344,38 +347,17 @@ void TiledLayerChromium::protectTileTextures(const IntRect& contentRect)
     }
 }
 
-void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
+void TiledLayerChromium::prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom)
 {
-    if (!m_tiler)
-        createTiler(isNonCompositedContent() ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels);
-
-    ASSERT(m_tiler);
-
-    m_skipsDraw = false;
-
     // Reset m_updateRect for all tiles.
     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
         tile->m_updateRect = IntRect();
     }
 
-    if (contentRect.isEmpty()) {
-        m_requestedUpdateRect = IntRect();
-        return;
-    }
-
-    m_tiler->growLayerToContain(contentRect);
-
-    if (!m_tiler->numTiles()) {
-        m_requestedUpdateRect = IntRect();
-        return;
-    }
-
     // Create tiles as needed, expanding a dirty rect to contain all
     // the dirty regions currently being drawn.
     IntRect dirtyLayerRect;
-    int left, top, right, bottom;
-    m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
@@ -386,8 +368,11 @@ void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
                 tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile);
 
             if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
-                m_skipsDraw = true;
-                cleanupResources();
+                m_skipsIdlePaint = true;
+                if (!idle) {
+                    m_skipsDraw = true;
+                    cleanupResources();
+                }
                 return;
             }
 
@@ -395,22 +380,22 @@ void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
         }
     }
 
-    // Due to borders, when the paint rect is extended to tile boundaries, it
-    // may end up overlapping more tiles than the original content rect. Record
-    // that original rect so we don't upload more tiles than necessary.
-    m_requestedUpdateRect = contentRect;
-
     m_paintRect = m_tiler->layerRectToContentRect(dirtyLayerRect);
     if (dirtyLayerRect.isEmpty())
         return;
 
+    // Due to borders, when the paint rect is extended to tile boundaries, it
+    // may end up overlapping more tiles than the original content rect. Record
+    // the original tiles so we don't upload more tiles than necessary.
+    if (!m_paintRect.isEmpty())
+        m_requestedUpdateTilesRect = IntRect(left, top, right - left + 1, bottom - top + 1);
+
     // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
     // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
     // However, we can't free the memory backing the GraphicsContext until the paint finishes,
     // so we grab a local reference here to hold the updater alive until the paint completes.
     RefPtr<LayerTextureUpdater> protector(textureUpdater());
     textureUpdater()->prepareToUpdate(m_paintRect, m_tiler->tileSize(), m_tiler->hasBorderTexels(), contentsScale());
-    m_tiler->contentRectToTileIndices(m_requestedUpdateRect, left, top, right, bottom);
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
@@ -442,5 +427,117 @@ void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
     }
 }
 
+
+void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
+{
+    if (!m_tiler)
+        createTiler(isNonCompositedContent() ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels);
+
+    ASSERT(m_tiler);
+
+    m_skipsDraw = false;
+    m_skipsIdlePaint = false;
+    m_requestedUpdateTilesRect = IntRect();
+    m_paintRect = IntRect();
+
+    m_tiler->growLayerToContain(IntRect(IntPoint::zero(), contentBounds()));
+
+    if (contentRect.isEmpty() || !m_tiler->numTiles())
+        return;
+
+    int left, top, right, bottom;
+    m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+
+    prepareToUpdateTiles(false, left, top, right, bottom);
+}
+
+void TiledLayerChromium::prepareToUpdateIdle(const IntRect& contentRect)
+{
+    // Abort if we have already prepared a paint or run out of memory.
+    if (m_skipsIdlePaint || !m_paintRect.isEmpty())
+        return;
+
+    ASSERT(m_tiler);
+
+    m_tiler->growLayerToContain(IntRect(IntPoint::zero(), contentBounds()));
+
+    if (!m_tiler->numTiles())
+        return;
+
+    // Protect any textures in the pre-paint area so we don't end up just
+    // reclaiming them below.
+    IntRect idlePaintContentRect = idlePaintRect(contentRect);
+    protectTileTextures(idlePaintContentRect);
+
+    // Expand outwards until we find a dirty row or column to update.
+    int left, top, right, bottom;
+    m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+    int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
+    m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
+    while (!m_skipsIdlePaint && (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom)) {
+        if (bottom < prepaintBottom) {
+            ++bottom;
+            prepareToUpdateTiles(true, left, bottom, right, bottom);
+            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+                break;
+        }
+        if (top > prepaintTop) {
+            --top;
+            prepareToUpdateTiles(true, left, top, right, top);
+            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+                break;
+        }
+        if (left > prepaintLeft) {
+            --left;
+            prepareToUpdateTiles(true, left, top, left, bottom);
+            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+                break;
+        }
+        if (right < prepaintRight) {
+            ++right;
+            prepareToUpdateTiles(true, right, top, right, bottom);
+            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
+                break;
+        }
+    }
+}
+
+bool TiledLayerChromium::needsIdlePaint(const IntRect& contentRect)
+{
+    if (m_skipsIdlePaint)
+        return false;
+
+    ASSERT(m_tiler);
+
+    IntRect idlePaintContentRect = idlePaintRect(contentRect);
+
+    int left, top, right, bottom;
+    m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom);
+    for (int j = top; j <= bottom; ++j) {
+        for (int i = left; i <= right; ++i) {
+            if (m_requestedUpdateTilesRect.contains(IntPoint(i, j)))
+                continue;
+            UpdatableTile* tile = tileAt(i, j);
+            if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat) || tile->isDirty())
+                return true;
+        }
+    }
+    return false;
+}
+
+IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleContentRect)
+{
+    ASSERT(m_tiler);
+    IntRect prepaintRect = visibleContentRect;
+    // FIXME: This can be made a lot larger if we can:
+    // - reserve memory at a lower priority than for visible content
+    // - only reserve idle paint tiles up to a memory reclaim threshold and
+    // - insure we play nicely with other layers
+    prepaintRect.inflateX(m_tiler->tileSize().width());
+    prepaintRect.inflateY(m_tiler->tileSize().height());
+    prepaintRect.intersect(IntRect(IntPoint::zero(), contentBounds()));
+    return prepaintRect;
+}
+
 }
 #endif // USE(ACCELERATED_COMPOSITING)
index 77a9ec9..4b0cc55 100644 (file)
@@ -75,9 +75,16 @@ protected:
 
     // Set invalidations to be potentially repainted during update().
     void invalidateRect(const IntRect& contentRect);
+
     // Prepare data needed to update textures that intersect with contentRect.
     void prepareToUpdate(const IntRect& contentRect);
 
+    // Same as above, but this will try to paint additional surrounding content if idle.
+    void prepareToUpdateIdle(const IntRect& contentRect);
+
+    // After preparing an update, returns true if more pre-painting is needed.
+    bool needsIdlePaint(const IntRect& contentRect);
+
     virtual void protectVisibleTileTextures();
 
     virtual TextureManager* textureManager() const;
@@ -90,17 +97,22 @@ private:
     void createTilerIfNeeded();
     void setTilingOption(TilingOption);
 
+    void prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom);
+    IntRect idlePaintRect(const IntRect& visibleContentRect);
+
     UpdatableTile* tileAt(int, int) const;
     UpdatableTile* createTile(int, int);
 
     // Temporary state held between prepareToUpdate() and updateCompositorResources().
-    IntRect m_requestedUpdateRect;
+    IntRect m_requestedUpdateTilesRect;
+
     // State held between prepareToUpdate() and pushPropertiesTo(). This represents the area
     // of the layer that is actually re-painted by WebKit.
     IntRect m_paintRect;
 
     GC3Denum m_textureFormat;
     bool m_skipsDraw;
+    bool m_skipsIdlePaint;
     LayerTextureUpdater::SampledTexelFormat m_sampledTexelFormat;
 
     TilingOption m_tilingOption;
index 7906440..f8d063f 100644 (file)
@@ -67,6 +67,7 @@ CCLayerTreeHost::CCLayerTreeHost(CCLayerTreeHostClient* client, const CCSettings
     , m_pageScale(1)
     , m_minPageScale(1)
     , m_maxPageScale(1)
+    , m_triggerIdlePaints(true)
 {
     ASSERT(CCProxy::isMainThread());
     numLayerTreeInstances++;
@@ -190,7 +191,9 @@ GraphicsContext3D* CCLayerTreeHost::context()
 
 bool CCLayerTreeHost::compositeAndReadback(void *pixels, const IntRect& rect)
 {
+    m_triggerIdlePaints = false;
     return m_proxy->compositeAndReadback(pixels, rect);
+    m_triggerIdlePaints = true;
 }
 
 void CCLayerTreeHost::finishAllRendering()
@@ -375,10 +378,36 @@ void CCLayerTreeHost::updateLayers(LayerChromium* rootLayer)
         CCLayerTreeHostCommon::calculateDrawTransformsAndVisibility(rootLayer, rootLayer, identityMatrix, identityMatrix, m_updateList, rootRenderSurface->layerList(), layerRendererCapabilities().maxTextureSize);
     }
 
-    paintLayerContents(m_updateList);
+    paintLayerContents(m_updateList, PaintVisible);
+    if (!m_triggerIdlePaints)
+        return;
+
+    size_t preferredLimitBytes = TextureManager::reclaimLimitBytes(m_viewportSize);
+    size_t maxLimitBytes = TextureManager::highLimitBytes(m_viewportSize);
+    contentsTextureManager()->reduceMemoryToLimit(preferredLimitBytes);
+    if (contentsTextureManager()->currentMemoryUseBytes() >= preferredLimitBytes)
+        return;
+
+    // Idle painting should fail when we hit the preferred memory limit,
+    // otherwise it will always push us towards the maximum limit.
+    m_contentsTextureManager->setMaxMemoryLimitBytes(preferredLimitBytes);
+    // The second (idle) paint will be a no-op in layers where painting already occured above.
+    paintLayerContents(m_updateList, PaintIdle);
+    m_contentsTextureManager->setMaxMemoryLimitBytes(maxLimitBytes);
+}
+
+// static
+void CCLayerTreeHost::paintContentsIfDirty(LayerChromium* layer, PaintType paintType)
+{
+    ASSERT(layer);
+    ASSERT(PaintVisible == paintType || PaintIdle == paintType);
+    if (PaintVisible == paintType)
+        layer->paintContentsIfDirty();
+    else
+        layer->idlePaintContentsIfDirty();
 }
 
-void CCLayerTreeHost::paintMaskAndReplicaForRenderSurface(LayerChromium* renderSurfaceLayer)
+void CCLayerTreeHost::paintMaskAndReplicaForRenderSurface(LayerChromium* renderSurfaceLayer, PaintType paintType)
 {
     // Note: Masks and replicas only exist for layers that own render surfaces. If we reach this point
     // in code, we already know that at least something will be drawn into this render surface, so the
@@ -386,22 +415,22 @@ void CCLayerTreeHost::paintMaskAndReplicaForRenderSurface(LayerChromium* renderS
 
     if (renderSurfaceLayer->maskLayer()) {
         renderSurfaceLayer->maskLayer()->setVisibleLayerRect(IntRect(IntPoint(), renderSurfaceLayer->contentBounds()));
-        renderSurfaceLayer->maskLayer()->paintContentsIfDirty();
+        paintContentsIfDirty(renderSurfaceLayer->maskLayer(), paintType);
     }
 
     LayerChromium* replicaLayer = renderSurfaceLayer->replicaLayer();
     if (replicaLayer) {
 
-        replicaLayer->paintContentsIfDirty();
+        paintContentsIfDirty(replicaLayer, paintType);
 
         if (replicaLayer->maskLayer()) {
             replicaLayer->maskLayer()->setVisibleLayerRect(IntRect(IntPoint(), replicaLayer->maskLayer()->contentBounds()));
-            replicaLayer->maskLayer()->paintContentsIfDirty();
+            paintContentsIfDirty(replicaLayer->maskLayer(), paintType);
         }
     }
 }
 
-void CCLayerTreeHost::paintLayerContents(const LayerList& renderSurfaceLayerList)
+void CCLayerTreeHost::paintLayerContents(const LayerList& renderSurfaceLayerList, PaintType paintType)
 {
     for (int surfaceIndex = renderSurfaceLayerList.size() - 1; surfaceIndex >= 0 ; --surfaceIndex) {
         LayerChromium* renderSurfaceLayer = renderSurfaceLayerList[surfaceIndex].get();
@@ -409,7 +438,7 @@ void CCLayerTreeHost::paintLayerContents(const LayerList& renderSurfaceLayerList
         ASSERT(renderSurface);
         ASSERT(renderSurface->drawOpacity());
 
-        paintMaskAndReplicaForRenderSurface(renderSurfaceLayer);
+        paintMaskAndReplicaForRenderSurface(renderSurfaceLayer, paintType);
 
         const LayerList& layerList = renderSurface->layerList();
         for (unsigned layerIndex = 0; layerIndex < layerList.size(); ++layerIndex) {
@@ -420,10 +449,9 @@ void CCLayerTreeHost::paintLayerContents(const LayerList& renderSurfaceLayerList
             if (CCLayerTreeHostCommon::renderSurfaceContributesToTarget<LayerChromium>(layer, renderSurfaceLayer->id()))
                 continue;
 
-            ASSERT(layer->opacity());
             ASSERT(!layer->bounds().isEmpty());
 
-            layer->paintContentsIfDirty();
+            paintContentsIfDirty(layer, paintType);
         }
     }
 }
index 2af4266..65cf538 100644 (file)
@@ -202,8 +202,10 @@ protected:
 private:
     typedef Vector<RefPtr<LayerChromium> > LayerList;
 
-    void paintLayerContents(const LayerList&);
-    void paintMaskAndReplicaForRenderSurface(LayerChromium*);
+    enum PaintType { PaintVisible, PaintIdle };
+    static void paintContentsIfDirty(LayerChromium*, PaintType);
+    void paintLayerContents(const LayerList&, PaintType);
+    void paintMaskAndReplicaForRenderSurface(LayerChromium*, PaintType);
 
     void updateLayers(LayerChromium*);
     void clearPendingUpdate();
@@ -234,6 +236,7 @@ private:
 
     float m_pageScale;
     float m_minPageScale, m_maxPageScale;
+    bool m_triggerIdlePaints;
 };
 
 }
index 26f581a..74942e3 100644 (file)
@@ -1,3 +1,21 @@
+2011-12-16  Eric Penner  <epenner@google.com>
+
+        [chromium] Need to prepaint tiles in TiledLayerChromium
+        https://bugs.webkit.org/show_bug.cgi?id=72686
+
+        Reviewed by James Robinson.
+
+        * tests/CCLayerTreeHostTest.cpp:
+        (WTF::ContentLayerChromiumWithUpdateTracking::idlePaintContentsCount):
+        (WTF::ContentLayerChromiumWithUpdateTracking::resetPaintContentsCount):
+        (WTF::ContentLayerChromiumWithUpdateTracking::idlePaintContentsIfDirty):
+        (WTF::ContentLayerChromiumWithUpdateTracking::ContentLayerChromiumWithUpdateTracking):
+        (WTF::CCLayerTreeHostTestOpacityChange::afterTest):
+        * tests/TiledLayerChromiumTest.cpp:
+        (WTF::FakeTiledLayerChromium::prepareToUpdateIdle):
+        (WTF::FakeTiledLayerChromium::needsIdlePaint):
+        (WTF::TEST):
+
 2011-12-16  James Robinson  <jamesr@chromium.org>
 
         [chromium] Remove WebCString's dependency on WebCore
index 0f1d9e3..45736e0 100644 (file)
@@ -953,7 +953,8 @@ public:
     static PassRefPtr<ContentLayerChromiumWithUpdateTracking> create(CCLayerDelegate *delegate) { return adoptRef(new ContentLayerChromiumWithUpdateTracking(delegate)); }
 
     int paintContentsCount() { return m_paintContentsCount; }
-    void resetPaintContentsCount() { m_paintContentsCount = 0; }
+    int idlePaintContentsCount() { return m_idlePaintContentsCount; }
+    void resetPaintContentsCount() { m_paintContentsCount = 0; m_idlePaintContentsCount = 0;}
 
     int updateCount() { return m_updateCount; }
     void resetUpdateCount() { m_updateCount = 0; }
@@ -964,6 +965,12 @@ public:
         m_paintContentsCount++;
     }
 
+    virtual void idlePaintContentsIfDirty()
+    {
+        ContentLayerChromium::idlePaintContentsIfDirty();
+        m_idlePaintContentsCount++;
+    }
+
     virtual void updateCompositorResources(GraphicsContext3D* context, CCTextureUpdater& updater)
     {
         ContentLayerChromium::updateCompositorResources(context, updater);
@@ -974,12 +981,14 @@ private:
     explicit ContentLayerChromiumWithUpdateTracking(CCLayerDelegate *delegate)
         : ContentLayerChromium(delegate)
         , m_paintContentsCount(0)
+        , m_idlePaintContentsCount(0)
         , m_updateCount(0)
     {
         setBounds(IntSize(10, 10));
     }
 
     int m_paintContentsCount;
+    int m_idlePaintContentsCount;
     int m_updateCount;
 };
 
@@ -1010,6 +1019,9 @@ public:
         // paintContentsIfDirty() should have been called once.
         EXPECT_EQ(1, m_updateCheckLayer->paintContentsCount());
 
+        // idlePaintContentsIfDirty() 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(),
index 49ce8df..8ab63c8 100644 (file)
@@ -97,6 +97,16 @@ public:
         TiledLayerChromium::prepareToUpdate(rect);
     }
 
+    void prepareToUpdateIdle(const IntRect& rect)
+    {
+        TiledLayerChromium::prepareToUpdateIdle(rect);
+    }
+
+    bool needsIdlePaint(const IntRect& rect)
+    {
+        return TiledLayerChromium::needsIdlePaint(rect);
+    }
+
     virtual TextureManager* textureManager() const { return m_textureManager; }
 
 private:
@@ -122,6 +132,7 @@ TEST(TiledLayerChromiumTest, pushDirtyTiles)
     CCTextureUpdater updater(&textureAllocator);
 
     // 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));
     layer->updateCompositorResources(0, updater);
@@ -145,5 +156,107 @@ TEST(TiledLayerChromiumTest, pushDirtyTiles)
     EXPECT_FALSE(layerImpl->hasTileAt(0, 1));
 }
 
+TEST(TiledLayerChromiumTest, pushIdlePaintTiles)
+{
+    OwnPtr<TextureManager> textureManager = TextureManager::create(4*1024*1024, 2*1024*1024, 1024);
+    RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get()));
+    DebugScopedSetImplThread implThread;
+    RefPtr<FakeCCTiledLayerImpl> layerImpl = adoptRef(new FakeCCTiledLayerImpl(0));
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    // 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);
+
+    // This invalidates 25 tiles and then paints one visible tile.
+    layer->setBounds(contentBounds);
+    layer->setVisibleLayerRect(visibleRect);
+    layer->invalidateRect(contentRect);
+    layer->prepareToUpdate(visibleRect);
+
+    // 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.
+    EXPECT_TRUE(layerImpl->hasTileAt(2, 2));
+
+    textureManager->unprotectAllTextures();
+
+    // For the next four updates, we should detect we still need idle painting.
+    for (int i = 0; i < 4; i++) {
+        layer->prepareToUpdate(visibleRect);
+        EXPECT_TRUE(layer->needsIdlePaint(visibleRect));
+        layer->prepareToUpdateIdle(visibleRect);
+        layer->updateCompositorResources(0, updater);
+        layer->pushPropertiesTo(layerImpl.get());
+        textureManager->unprotectAllTextures();
+    }
+
+    // After four passes of idle painting, we should be finished painting
+    EXPECT_FALSE(layer->needsIdlePaint(visibleRect));
+
+    // We should have one tile surrounding the visible tile on all sides, but no other tiles.
+    IntRect idlePaintTiles(1, 1, 3, 3);
+    for (int i = 0; i < 5; i++) {
+        for (int j = 0; j < 5; j++) {
+            if (idlePaintTiles.contains(i, j))
+                EXPECT_TRUE(layerImpl->hasTileAt(i, j));
+            else
+                EXPECT_FALSE(layerImpl->hasTileAt(i, j));
+        }
+    }
+}
+
+
+TEST(TiledLayerChromiumTest, idlePaintOutOfMemory)
+{
+    // The tile size is 100x100. Setup 5x5 tiles with one 1x1 visible tile in the center.
+    IntSize contentBounds(300, 300);
+    IntRect contentRect(IntPoint::zero(), contentBounds);
+    IntRect visibleRect(100, 100, 100, 100);
+
+    // 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.
+
+    OwnPtr<TextureManager> textureManager = TextureManager::create(memoryLimit, memoryLimit / 2, 1024);
+    RefPtr<FakeTiledLayerChromium> layer = adoptRef(new FakeTiledLayerChromium(textureManager.get()));
+    DebugScopedSetImplThread implThread;
+    RefPtr<FakeCCTiledLayerImpl> layerImpl = adoptRef(new FakeCCTiledLayerImpl(0));
+
+    FakeTextureAllocator textureAllocator;
+    CCTextureUpdater updater(&textureAllocator);
+
+    // This invalidates 9 tiles and then paints one visible tile.
+    layer->setBounds(contentBounds);
+    layer->setVisibleLayerRect(visibleRect);
+    layer->invalidateRect(contentRect);
+    layer->prepareToUpdate(visibleRect);
+
+    // 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);
+    layer->prepareToUpdateIdle(visibleRect);
+
+    // 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());
+}
+
 } // namespace