Unreviewed, rolling out r105366.
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / TiledLayerChromium.cpp
index 87dec0b..713b4e0 100644 (file)
@@ -33,6 +33,7 @@
 #include "LayerRendererChromium.h"
 #include "ManagedTexture.h"
 #include "MathExtras.h"
+#include "Region.h"
 #include "TextStream.h"
 #include "cc/CCLayerImpl.h"
 #include "cc/CCTextureUpdater.h"
@@ -52,27 +53,33 @@ namespace WebCore {
 class UpdatableTile : public CCLayerTilingData::Tile {
     WTF_MAKE_NONCOPYABLE(UpdatableTile);
 public:
-    explicit UpdatableTile(PassOwnPtr<ManagedTexture> tex) : m_tex(tex) { }
+    explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture) : m_texture(texture) { }
 
-    ManagedTexture* texture() { return m_tex.get(); }
+    LayerTextureUpdater::Texture* texture() { return m_texture.get(); }
+    ManagedTexture* managedTexture() { return m_texture->texture(); }
 
-    bool dirty() const { return !m_dirtyLayerRect.isEmpty(); }
-    void clearDirty() { m_dirtyLayerRect = IntRect(); }
+    bool isDirty() const { return !m_dirtyRect.isEmpty(); }
+    void copyAndClearDirty()
+    {
+        m_updateRect = m_dirtyRect;
+        m_dirtyRect = IntRect();
+    }
 
-    // Layer-space dirty rectangle that needs to be repainted.
-    IntRect m_dirtyLayerRect;
+    IntRect m_dirtyRect;
+    IntRect m_updateRect;
 private:
-    OwnPtr<ManagedTexture> m_tex;
+    OwnPtr<LayerTextureUpdater::Texture> m_texture;
 };
 
 TiledLayerChromium::TiledLayerChromium(CCLayerDelegate* delegate)
     : LayerChromium(delegate)
-    , m_tilingOption(AutoTile)
     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
     , m_skipsDraw(false)
-    , m_textureOrientation(LayerTextureUpdater::InvalidOrientation)
+    , m_skipsIdlePaint(false)
     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
+    , m_tilingOption(AutoTile)
 {
+    m_tiler = CCLayerTilingData::create(IntSize(defaultTileSize, defaultTileSize), CCLayerTilingData::HasBorderTexels);
 }
 
 TiledLayerChromium::~TiledLayerChromium()
@@ -88,16 +95,13 @@ void TiledLayerChromium::cleanupResources()
 {
     LayerChromium::cleanupResources();
 
-    m_tiler.clear();
+    m_tiler->reset();
     m_paintRect = IntRect();
-    m_requestedUpdateRect = IntRect();
+    m_requestedUpdateTilesRect = IntRect();
 }
 
 void TiledLayerChromium::updateTileSizeAndTilingOption()
 {
-    if (!m_tiler)
-        return;
-
     const IntSize tileSize(min(defaultTileSize, contentBounds().width()), min(defaultTileSize, contentBounds().height()));
 
     // Tile if both dimensions large, or any one dimension large and the other
@@ -118,17 +122,41 @@ void TiledLayerChromium::updateTileSizeAndTilingOption()
     IntSize requestedSize = isTiled ? tileSize : contentBounds();
     const int maxSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize;
     IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
-    m_tiler->setTileSize(clampedSize);
+    setTileSize(clampedSize);
+}
+
+void TiledLayerChromium::updateBounds()
+{
+    IntSize oldBounds = m_tiler->bounds();
+    IntSize newBounds = contentBounds();
+    if (oldBounds == newBounds)
+        return;
+    m_tiler->setBounds(newBounds);
+
+    // Invalidate any areas that the new bounds exposes.
+    Region oldRegion(IntRect(IntPoint(), oldBounds));
+    Region newRegion(IntRect(IntPoint(), newBounds));
+    newRegion.subtract(oldRegion);
+    Vector<IntRect> rects = newRegion.rects();
+    for (size_t i = 0; i < rects.size(); ++i)
+        invalidateRect(rects[i]);
+}
+
+void TiledLayerChromium::setTileSize(const IntSize& size)
+{
+    m_tiler->setTileSize(size);
+}
+
+void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOption borderTexelOption)
+{
+    m_tiler->setBorderTexelOption(borderTexelOption);
 }
 
 bool TiledLayerChromium::drawsContent() const
 {
-    if (!m_delegate)
+    if (!LayerChromium::drawsContent() || !m_delegate)
         return false;
 
-    if (!m_tiler)
-        return true;
-
     if (m_tilingOption == NeverTile && m_tiler->numTiles() > 1)
         return false;
 
@@ -147,48 +175,48 @@ IntSize TiledLayerChromium::contentBounds() const
 
 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
 {
+    if (host == layerTreeHost())
+        return;
+
+    if (layerTreeHost())
+        cleanupResources();
+
     LayerChromium::setLayerTreeHost(host);
 
-    if (m_tiler || !host)
+    if (!host)
         return;
 
     createTextureUpdater(host);
-
-    m_textureFormat = host->layerRendererCapabilities().bestTextureFormat;
-    m_textureOrientation = textureUpdater()->orientation();
+    setTextureFormat(host->layerRendererCapabilities().bestTextureFormat);
     m_sampledTexelFormat = textureUpdater()->sampledTexelFormat(m_textureFormat);
-    m_tiler = CCLayerTilingData::create(
-        IntSize(defaultTileSize, defaultTileSize),
-        isNonCompositedContent() ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels);
 }
 
-void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context, CCTextureUpdater& updater)
+void TiledLayerChromium::updateCompositorResources(GraphicsContext3D*, CCTextureUpdater& updater)
 {
     // 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->numTiles())
+    if (m_skipsDraw || m_requestedUpdateTilesRect.isEmpty() || m_tiler->isEmpty())
         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);
+
+            // Required tiles are created in prepareToUpdate(). A tile should
+            // never be removed between the call to prepareToUpdate() and the
+            // call to updateCompositorResources().
             if (!tile)
-                tile = createTile(i, j);
-            else if (!tile->dirty())
-                continue;
+                CRASH();
 
-            // Calculate page-space rectangle to copy from.
-            IntRect sourceRect = m_tiler->tileContentRect(tile);
-            const IntPoint anchor = sourceRect.location();
-            sourceRect.intersect(m_tiler->layerRectToContentRect(tile->m_dirtyLayerRect));
-            // Paint rect not guaranteed to line up on tile boundaries, so
-            // make sure that sourceRect doesn't extend outside of it.
-            sourceRect.intersect(m_paintRect);
-            if (sourceRect.isEmpty())
+            IntRect sourceRect = tile->m_updateRect;
+            if (tile->m_updateRect.isEmpty())
                 continue;
 
-            ASSERT(tile->texture()->isReserved());
+            ASSERT(tile->managedTexture()->isReserved());
+            const IntPoint anchor = m_tiler->tileRect(tile).location();
 
             // Calculate tile-space rectangle to upload into.
             IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size());
@@ -208,23 +236,16 @@ void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context, C
             if (paintOffset.y() + destRect.height() > m_paintRect.height())
                 CRASH();
 
-            tile->texture()->bindTexture(context, updater.allocator());
-            const GC3Dint filter = m_tiler->hasBorderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST;
-            GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, filter));
-            GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, filter));
-
-            updater.append(tile->texture(), textureUpdater(), sourceRect, destRect);
-            tile->clearDirty();
+            updater.append(tile->texture(), sourceRect, destRect);
         }
     }
 
-    m_updateRect = FloatRect(m_tiler->contentRectToLayerRect(m_paintRect));
+    m_updateRect = FloatRect(m_paintRect);
 }
 
 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
 {
     m_tilingOption = tilingOption;
-    updateTileSizeAndTilingOption();
 }
 
 void TiledLayerChromium::setIsMask(bool isMask)
@@ -237,24 +258,21 @@ void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
     LayerChromium::pushPropertiesTo(layer);
 
     CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer);
-    if (!m_tiler) {
-        tiledLayer->setSkipsDraw(true);
-        return;
-    }
 
     tiledLayer->setSkipsDraw(m_skipsDraw);
-    tiledLayer->setTextureOrientation(m_textureOrientation);
-    tiledLayer->setSampledTexelFormat(m_sampledTexelFormat);
+    tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA);
     tiledLayer->setTilingData(*m_tiler);
 
     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
         int i = iter->first.first;
         int j = iter->first.second;
         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
-        if (!tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
+        if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
+            continue;
+        if (tile->isDirty())
             continue;
 
-        tiledLayer->syncTextureId(i, j, tile->texture()->textureId());
+        tiledLayer->syncTextureId(i, j, tile->managedTexture()->textureId());
     }
 }
 
@@ -272,34 +290,49 @@ UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
 
 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
 {
-    RefPtr<UpdatableTile> tile = adoptRef(new UpdatableTile(ManagedTexture::create(textureManager())));
+    RefPtr<UpdatableTile> tile = adoptRef(new UpdatableTile(textureUpdater()->createTexture(textureManager())));
     m_tiler->addTile(tile, i, j);
-    tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile.get());
+    tile->m_dirtyRect = m_tiler->tileRect(tile.get());
 
     return tile.get();
 }
 
-void TiledLayerChromium::invalidateRect(const IntRect& contentRect)
+void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
 {
-    if (!m_tiler || contentRect.isEmpty() || m_skipsDraw)
-        return;
+    IntRect dirty = enclosingIntRect(dirtyRect);
+    invalidateRect(dirty);
+    LayerChromium::setNeedsDisplayRect(dirtyRect);
+}
 
-    m_tiler->growLayerToContain(contentRect);
+void TiledLayerChromium::setIsNonCompositedContent(bool isNonCompositedContent)
+{
+    LayerChromium::setIsNonCompositedContent(isNonCompositedContent);
+
+    CCLayerTilingData::BorderTexelOption borderTexelOption;
+#if OS(ANDROID)
+    // Always want border texels and GL_LINEAR due to pinch zoom.
+    borderTexelOption = CCLayerTilingData::HasBorderTexels;
+#else
+    borderTexelOption = isNonCompositedContent ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels;
+#endif
+    setBorderTexelOption(borderTexelOption);
+}
 
-    // Dirty rects are always in layer space, as the layer could be repositioned
-    // after invalidation.
-    const IntRect layerRect = m_tiler->contentRectToLayerRect(contentRect);
+void TiledLayerChromium::invalidateRect(const IntRect& layerRect)
+{
+    if (m_tiler->isEmpty() || layerRect.isEmpty() || m_skipsDraw)
+        return;
 
     int left, top, right, bottom;
-    m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
             if (!tile)
                 continue;
-            IntRect bound = m_tiler->tileLayerRect(tile);
+            IntRect bound = m_tiler->tileRect(tile);
             bound.intersect(layerRect);
-            tile->m_dirtyLayerRect.unite(bound);
+            tile->m_dirtyRect.unite(bound);
         }
     }
 }
@@ -309,82 +342,208 @@ void TiledLayerChromium::protectVisibleTileTextures()
     protectTileTextures(IntRect(IntPoint::zero(), contentBounds()));
 }
 
-void TiledLayerChromium::protectTileTextures(const IntRect& contentRect)
+void TiledLayerChromium::protectTileTextures(const IntRect& layerRect)
 {
-    if (!m_tiler || contentRect.isEmpty())
+    if (m_tiler->isEmpty() || layerRect.isEmpty())
         return;
 
     int left, top, right, bottom;
-    m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
 
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
-            if (!tile || !tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
+            if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
                 continue;
 
-            tile->texture()->reserve(m_tiler->tileSize(), m_textureFormat);
+            tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat);
         }
     }
 }
 
-void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
+void TiledLayerChromium::prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom)
 {
-    ASSERT(m_tiler);
-
-    m_skipsDraw = false;
-
-    if (contentRect.isEmpty()) {
-        m_requestedUpdateRect = IntRect();
-        return;
-    }
-
-    m_tiler->growLayerToContain(contentRect);
-
-    if (!m_tiler->numTiles()) {
-        m_requestedUpdateRect = IntRect();
-        return;
+    // 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();
     }
 
     // Create tiles as needed, expanding a dirty rect to contain all
-    // the dirty regions currently being drawn.
+    // the dirty regions currently being drawn. All dirty tiles that are to be painted
+    // get their m_updateRect set to m_dirtyRect and m_dirtyRect cleared. This way if
+    // invalidateRect is invoked during prepareToUpdate we don't lose the request.
     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);
             if (!tile)
                 tile = createTile(i, j);
 
-            if (!tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
-                tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile);
+            if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
+                tile->m_dirtyRect = m_tiler->tileRect(tile);
 
-            if (!tile->texture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
-                m_skipsDraw = true;
-                cleanupResources();
+            if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
+                m_skipsIdlePaint = true;
+                if (!idle) {
+                    m_skipsDraw = true;
+                    cleanupResources();
+                }
                 return;
             }
 
-            dirtyLayerRect.unite(tile->m_dirtyLayerRect);
+            dirtyLayerRect.unite(tile->m_dirtyRect);
+            tile->copyAndClearDirty();
         }
     }
 
-    // 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);
+    m_paintRect = 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());
+    IntRect opaqueRect; // FIXME: unused. remove this and store in the layer to pass to impl for draw culling
+    textureUpdater()->prepareToUpdate(m_paintRect, m_tiler->tileSize(), m_tiler->hasBorderTexels(), contentsScale(), &opaqueRect);
+    for (int j = top; j <= bottom; ++j) {
+        for (int i = left; i <= right; ++i) {
+            UpdatableTile* tile = tileAt(i, j);
+
+            // Tiles are created before prepareToUpdate() is called.
+            if (!tile)
+                CRASH();
+
+            // Use m_updateRect as copyAndClearDirty above moved the existing dirty rect to m_updateRect.
+            const IntRect& dirtyRect = tile->m_updateRect;
+            if (dirtyRect.isEmpty())
+                continue;
+
+            IntRect sourceRect = m_tiler->tileRect(tile);
+            sourceRect.intersect(dirtyRect);
+            // Paint rect not guaranteed to line up on tile boundaries, so
+            // make sure that sourceRect doesn't extend outside of it.
+            sourceRect.intersect(m_paintRect);
+
+            tile->m_updateRect = sourceRect;
+            if (sourceRect.isEmpty())
+                continue;
+
+            tile->texture()->prepareRect(sourceRect);
+        }
+    }
+}
+
+
+void TiledLayerChromium::prepareToUpdate(const IntRect& layerRect)
+{
+    m_skipsDraw = false;
+    m_skipsIdlePaint = false;
+    m_requestedUpdateTilesRect = IntRect();
+    m_paintRect = IntRect();
+
+    updateBounds();
+
+    if (layerRect.isEmpty() || !m_tiler->numTiles())
+        return;
+
+    int left, top, right, bottom;
+    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
+
+    prepareToUpdateTiles(false, left, top, right, bottom);
+}
+
+void TiledLayerChromium::prepareToUpdateIdle(const IntRect& layerRect)
+{
+    // Abort if we have already prepared a paint or run out of memory.
+    if (m_skipsIdlePaint || !m_paintRect.isEmpty())
+        return;
+
+    ASSERT(m_tiler);
+
+    updateBounds();
+
+    if (m_tiler->isEmpty())
+        return;
+
+    // Protect any textures in the pre-paint area so we don't end up just
+    // reclaiming them below.
+    IntRect idlePaintLayerRect = idlePaintRect(layerRect);
+    protectTileTextures(idlePaintLayerRect);
+
+    // Expand outwards until we find a dirty row or column to update.
+    int left, top, right, bottom;
+    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
+    int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
+    m_tiler->layerRectToTileIndices(idlePaintLayerRect, 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& layerRect)
+{
+    if (m_skipsIdlePaint)
+        return false;
+
+    IntRect idlePaintLayerRect = idlePaintRect(layerRect);
+
+    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 (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& visibleLayerRect)
+{
+    IntRect prepaintRect = visibleLayerRect;
+    // 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;
 }
 
 }