Unreviewed, rolling out r125230 and r125238.
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / TiledLayerChromium.cpp
index 1430a61..ccbd58d 100644 (file)
 #include "TiledLayerChromium.h"
 
 #include "GraphicsContext3D.h"
-#include "LayerRendererChromium.h"
-#include "ManagedTexture.h"
-#include "MathExtras.h"
 #include "Region.h"
 #include "TextStream.h"
+
 #include "cc/CCLayerImpl.h"
-#include "cc/CCTextureUpdater.h"
+#include "cc/CCLayerTreeHost.h"
+#include "cc/CCOverdrawMetrics.h"
+#include "cc/CCTextureUpdateQueue.h"
 #include "cc/CCTiledLayerImpl.h"
-#include <wtf/CurrentTime.h>
 
-// Start tiling when the width and height of a layer are larger than this size.
-static int maxUntiledSize = 512;
-
-// When tiling is enabled, use tiles of this dimension squared.
-static int defaultTileSize = 256;
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
 
 using namespace std;
+using WebKit::WebTransformationMatrix;
 
 namespace WebCore {
 
 class UpdatableTile : public CCLayerTilingData::Tile {
     WTF_MAKE_NONCOPYABLE(UpdatableTile);
 public:
-    explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture) : m_texture(texture) { }
+    static PassOwnPtr<UpdatableTile> create(PassOwnPtr<LayerTextureUpdater::Texture> texture)
+    {
+        return adoptPtr(new UpdatableTile(texture));
+    }
 
     LayerTextureUpdater::Texture* texture() { return m_texture.get(); }
-    ManagedTexture* managedTexture() { return m_texture->texture(); }
+    CCPrioritizedTexture* managedTexture() { return m_texture->texture(); }
+
+    bool isDirty() const { return !dirtyRect.isEmpty(); }
 
-    bool isDirty() const { return !m_dirtyRect.isEmpty(); }
-    void copyAndClearDirty()
+    // Reset update state for the current frame. This should occur before painting
+    // for all layers. Since painting one layer can invalidate another layer
+    // after it has already painted, mark all non-dirty tiles as valid before painting
+    // such that invalidations during painting won't prevent them from being pushed.
+    void resetUpdateState()
     {
-        m_updateRect = m_dirtyRect;
-        m_dirtyRect = IntRect();
+        updateRect = IntRect();
+        occluded = false;
+        partialUpdate = false;
+        validForFrame = !isDirty();
     }
 
-    IntRect m_dirtyRect;
-    IntRect m_updateRect;
+    // This promises to update the tile and therefore also guarantees the tile
+    // will be valid for this frame. dirtyRect is copied into updateRect so
+    // we can continue to track re-entrant invalidations that occur during painting.
+    void markForUpdate()
+    {
+        validForFrame = true;
+        updateRect = dirtyRect;
+        dirtyRect = IntRect();
+    }
+
+    IntRect dirtyRect;
+    IntRect updateRect;
+    bool partialUpdate;
+    bool validForFrame;
+    bool occluded;
+    bool isInUseOnImpl;
 private:
+    explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture)
+        : partialUpdate(false)
+        , validForFrame(false)
+        , occluded(false)
+        , isInUseOnImpl(false)
+        , m_texture(texture)
+    {
+    }
+
     OwnPtr<LayerTextureUpdater::Texture> m_texture;
 };
 
@@ -75,40 +105,41 @@ TiledLayerChromium::TiledLayerChromium()
     : LayerChromium()
     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
     , m_skipsDraw(false)
-    , m_skipsIdlePaint(false)
+    , m_failedUpdate(false)
     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
     , m_tilingOption(AutoTile)
 {
-    m_tiler = CCLayerTilingData::create(IntSize(defaultTileSize, defaultTileSize), CCLayerTilingData::HasBorderTexels);
+    m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderTexels);
 }
 
 TiledLayerChromium::~TiledLayerChromium()
 {
 }
 
-PassRefPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
+PassOwnPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
 {
     return CCTiledLayerImpl::create(id());
 }
 
-void TiledLayerChromium::cleanupResources()
+void TiledLayerChromium::updateTileSizeAndTilingOption()
 {
-    LayerChromium::cleanupResources();
+    ASSERT(layerTreeHost());
 
-    m_tiler->reset();
-    m_paintRect = IntRect();
-    m_requestedUpdateTilesRect = IntRect();
-}
+    const IntSize& defaultTileSize = layerTreeHost()->settings().defaultTileSize;
+    const IntSize& maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledLayerSize;
+    int layerWidth = contentBounds().width();
+    int layerHeight = contentBounds().height();
 
-void TiledLayerChromium::updateTileSizeAndTilingOption()
-{
-    const IntSize tileSize(min(defaultTileSize, contentBounds().width()), min(defaultTileSize, contentBounds().height()));
+    const IntSize tileSize(min(defaultTileSize.width(), layerWidth), min(defaultTileSize.height(), layerHeight));
 
     // Tile if both dimensions large, or any one dimension large and the other
-    // extends into a second tile. This heuristic allows for long skinny layers
-    // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space.
-    const bool anyDimensionLarge = contentBounds().width() > maxUntiledSize || contentBounds().height() > maxUntiledSize;
-    const bool anyDimensionOneTile = contentBounds().width() <= defaultTileSize || contentBounds().height() <= defaultTileSize;
+    // extends into a second tile but the total layer area isn't larger than that
+    // of the largest possible untiled layer. This heuristic allows for long skinny layers
+    // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but still avoids
+    // creating very large tiles.
+    const bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || layerHeight > maxUntiledLayerSize.height();
+    const bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || layerHeight <= defaultTileSize.height())
+                                      && (layerWidth * layerHeight) <= (maxUntiledLayerSize.width() * maxUntiledLayerSize.height());
     const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
 
     bool isTiled;
@@ -139,7 +170,7 @@ void TiledLayerChromium::updateBounds()
     newRegion.subtract(oldRegion);
     Vector<IntRect> rects = newRegion.rects();
     for (size_t i = 0; i < rects.size(); ++i)
-        invalidateRect(rects[i]);
+        invalidateContentRect(rects[i]);
 }
 
 void TiledLayerChromium::setTileSize(const IntSize& size)
@@ -157,10 +188,11 @@ bool TiledLayerChromium::drawsContent() const
     if (!LayerChromium::drawsContent())
         return false;
 
-    if (m_tilingOption == NeverTile && m_tiler->numTiles() > 1)
+    bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() > 1;
+    if (m_tilingOption == NeverTile && hasMoreThanOneTile)
         return false;
 
-    return !m_skipsDraw;
+    return true;
 }
 
 bool TiledLayerChromium::needsContentsScale() const
@@ -173,76 +205,6 @@ IntSize TiledLayerChromium::contentBounds() const
     return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds().height() * contentsScale()));
 }
 
-void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
-{
-    if (host == layerTreeHost())
-        return;
-
-    if (layerTreeHost())
-        cleanupResources();
-
-    LayerChromium::setLayerTreeHost(host);
-
-    if (!host)
-        return;
-
-    createTextureUpdater(host);
-    setTextureFormat(host->layerRendererCapabilities().bestTextureFormat);
-    m_sampledTexelFormat = textureUpdater()->sampledTexelFormat(m_textureFormat);
-}
-
-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_requestedUpdateTilesRect.isEmpty() || m_tiler->isEmpty())
-        return;
-
-    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)
-                CRASH();
-
-            IntRect sourceRect = tile->m_updateRect;
-            if (tile->m_updateRect.isEmpty())
-                continue;
-
-            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());
-            if (destRect.x() < 0)
-                CRASH();
-            if (destRect.y() < 0)
-                CRASH();
-
-            // Offset from paint rectangle to this tile's dirty rectangle.
-            IntPoint paintOffset(sourceRect.x() - m_paintRect.x(), sourceRect.y() - m_paintRect.y());
-            if (paintOffset.x() < 0)
-                CRASH();
-            if (paintOffset.y() < 0)
-                CRASH();
-            if (paintOffset.x() + destRect.width() > m_paintRect.width())
-                CRASH();
-            if (paintOffset.y() + destRect.height() > m_paintRect.height())
-                CRASH();
-
-            updater.append(tile->texture(), sourceRect, destRect);
-        }
-    }
-
-    m_updateRect = FloatRect(m_paintRect);
-}
-
 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
 {
     m_tilingOption = tilingOption;
@@ -262,27 +224,51 @@ void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
     tiledLayer->setSkipsDraw(m_skipsDraw);
     tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA);
     tiledLayer->setTilingData(*m_tiler);
+    Vector<UpdatableTile*> invalidTiles;
 
     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->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
+        // FIXME: This should not ever be null.
+        if (!tile)
             continue;
-        if (tile->isDirty())
+        tile->isInUseOnImpl = false;
+        if (!tile->managedTexture()->haveBackingTexture()) {
+            invalidTiles.append(tile);
+            continue;
+        }
+        if (!tile->validForFrame)
             continue;
 
-        tiledLayer->syncTextureId(i, j, tile->managedTexture()->textureId());
+        tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(), tile->opaqueRect());
+        tile->isInUseOnImpl = true;
     }
+    for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter)
+        m_tiler->takeTile((*iter)->i(), (*iter)->j());
 }
 
-TextureManager* TiledLayerChromium::textureManager() const
+CCPrioritizedTextureManager* TiledLayerChromium::textureManager() const
 {
     if (!layerTreeHost())
         return 0;
     return layerTreeHost()->contentsTextureManager();
 }
 
+void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
+{
+    if (host && host != layerTreeHost()) {
+        for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
+            UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+            // FIXME: This should not ever be null.
+            if (!tile)
+                continue;
+            tile->managedTexture()->setTextureManager(host->contentsTextureManager());
+        }
+    }
+    LayerChromium::setLayerTreeHost(host);
+}
+
 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
 {
     return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
@@ -290,259 +276,511 @@ UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
 
 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
 {
-    RefPtr<UpdatableTile> tile = adoptRef(new UpdatableTile(textureUpdater()->createTexture(textureManager())));
-    m_tiler->addTile(tile, i, j);
-    tile->m_dirtyRect = m_tiler->tileRect(tile.get());
+    createTextureUpdaterIfNeeded();
+
+    OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTexture(textureManager())));
+    tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat);
 
-    return tile.get();
+    UpdatableTile* addedTile = tile.get();
+    m_tiler->addTile(tile.release(), i, j);
+
+    addedTile->dirtyRect = m_tiler->tileRect(addedTile);
+
+    // Temporary diagnostic crash.
+    if (!addedTile)
+        CRASH();
+    if (!tileAt(i, j))
+        CRASH();
+
+    return addedTile;
 }
 
 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
 {
-    IntRect dirty = enclosingIntRect(dirtyRect);
-    invalidateRect(dirty);
+    float contentsWidthScale = static_cast<float>(contentBounds().width()) / bounds().width();
+    float contentsHeightScale = static_cast<float>(contentBounds().height()) / bounds().height();
+    FloatRect scaledDirtyRect(dirtyRect);
+    scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale);
+    IntRect dirty = enclosingIntRect(scaledDirtyRect);
+    invalidateContentRect(dirty);
     LayerChromium::setNeedsDisplayRect(dirtyRect);
 }
 
-void TiledLayerChromium::setIsNonCompositedContent(bool isNonCompositedContent)
+void TiledLayerChromium::setUseLCDText(bool useLCDText)
 {
-    LayerChromium::setIsNonCompositedContent(isNonCompositedContent);
+    LayerChromium::setUseLCDText(useLCDText);
 
     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;
+    borderTexelOption = useLCDText ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels;
 #endif
     setBorderTexelOption(borderTexelOption);
 }
 
-void TiledLayerChromium::invalidateRect(const IntRect& layerRect)
+void TiledLayerChromium::invalidateContentRect(const IntRect& contentRect)
 {
-    if (m_tiler->isEmpty() || layerRect.isEmpty() || m_skipsDraw)
+    updateBounds();
+    if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw)
         return;
 
-    int 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->tileRect(tile);
-            bound.intersect(layerRect);
-            tile->m_dirtyRect.unite(bound);
-        }
+    for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
+        UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+        ASSERT(tile);
+        // FIXME: This should not ever be null.
+        if (!tile)
+            continue;
+        IntRect bound = m_tiler->tileRect(tile);
+        bound.intersect(contentRect);
+        tile->dirtyRect.unite(bound);
     }
 }
 
-void TiledLayerChromium::protectVisibleTileTextures()
+// Returns true if tile is dirty and only part of it needs to be updated.
+bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
 {
-    protectTileTextures(IntRect(IntPoint::zero(), contentBounds()));
+    return !tile->dirtyRect.contains(m_tiler->tileRect(tile));
 }
 
-void TiledLayerChromium::protectTileTextures(const IntRect& layerRect)
+// Dirty tiles with valid textures needs buffered update to guarantee that
+// we don't modify textures currently used for drawing by the impl thread.
+bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile)
 {
-    if (m_tiler->isEmpty() || layerRect.isEmpty())
-        return;
+    if (!tile->managedTexture()->haveBackingTexture())
+        return false;
 
-    int left, top, right, bottom;
-    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
+    if (!tile->isDirty())
+        return false;
+
+    if (!tile->isInUseOnImpl)
+        return false;
+
+    return true;
+}
+
+
+bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats, bool& didPaint)
+{
+    didPaint = false;
+    createTextureUpdaterIfNeeded();
+
+    bool ignoreOcclusions = !occlusion;
+    if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) {
+        m_failedUpdate = true;
+        return false;
+    }
+
+    IntRect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclusions);
+
+    if (occlusion)
+        occlusion->overdrawMetrics().didPaint(paintRect);
+
+    if (paintRect.isEmpty())
+        return true;
 
+    didPaint = true;
+    updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, stats);
+    return true;
+}
+
+void TiledLayerChromium::markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const CCOcclusionTracker* occlusion)
+{
+    // There is some difficult dependancies between occlusions, recording occlusion metrics
+    // and requesting memory so those are encapsulated in this function:
+    // - We only want to call requestLate on unoccluded textures (to preserve
+    //   memory for other layers when near OOM).
+    // - We only want to record occlusion metrics if all memory requests succeed.
+
+    int occludedTileCount = 0;
+    bool succeeded = true;
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
-            if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
+            ASSERT(tile); // Did setTexturePriorities get skipped?
+            // FIXME: This should not ever be null.
+            if (!tile)
                 continue;
-
-            tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat);
+            ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occluded pass?
+            IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), visibleContentRect());
+            if (occlusion && occlusion->occluded(this, visibleTileRect)) {
+                tile->occluded = true;
+                occludedTileCount++;
+            } else {
+                succeeded &= tile->managedTexture()->requestLate();
+            }
         }
     }
+
+    if (!succeeded)
+        return;
+
+    // FIXME: Remove the loop and just pass the count!
+    for (int i = 0; i < occludedTileCount; i++)
+        occlusion->overdrawMetrics().didCullTileForUpload();
 }
 
-void TiledLayerChromium::prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom)
+bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions)
 {
-    // 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. 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;
     for (int j = top; j <= bottom; ++j) {
         for (int i = left; i <= right; ++i) {
             UpdatableTile* tile = tileAt(i, j);
+            ASSERT(tile); // Did setTexturePriorites get skipped?
+            // FIXME: This should not ever be null.
             if (!tile)
-                tile = createTile(i, j);
+                continue;
 
-            if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
-                tile->m_dirtyRect = m_tiler->tileRect(tile);
+            // Ensure the entire tile is dirty if we don't have the texture.
+            if (!tile->managedTexture()->haveBackingTexture())
+                tile->dirtyRect = m_tiler->tileRect(tile);
 
-            if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
-                m_skipsIdlePaint = true;
-                if (!idle) {
-                    m_skipsDraw = true;
-                    cleanupResources();
-                }
-                return;
-            }
+            // If using occlusion and the visible region of the tile is occluded,
+            // don't reserve a texture or update the tile.
+            if (tile->occluded && !ignoreOcclusions)
+                continue;
 
-            dirtyLayerRect.unite(tile->m_dirtyRect);
-            tile->copyAndClearDirty();
+            if (!tile->managedTexture()->canAcquireBackingTexture())
+                return false;
         }
     }
+    return true;
+}
 
-    m_paintRect = dirtyLayerRect;
-    if (dirtyLayerRect.isEmpty())
-        return;
+IntRect TiledLayerChromium::markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions)
+{
+    IntRect paintRect;
+    for (int j = top; j <= bottom; ++j) {
+        for (int i = left; i <= right; ++i) {
+            UpdatableTile* tile = tileAt(i, j);
+            ASSERT(tile); // Did setTexturePriorites get skipped?
+            // FIXME: This should not ever be null.
+            if (!tile)
+                continue;
+            if (tile->occluded && !ignoreOcclusions)
+                continue;
+            paintRect.unite(tile->dirtyRect);
+            tile->markForUpdate();
+        }
+    }
+    return paintRect;
+}
 
-    // 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);
+void TiledLayerChromium::updateTileTextures(const IntRect& paintRect, int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
+{
+    // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space.
+    m_updateRect = FloatRect(paintRect);
+    float widthScale = bounds().width() / static_cast<float>(contentBounds().width());
+    float heightScale = bounds().height() / static_cast<float>(contentBounds().height());
+    m_updateRect.scale(widthScale, heightScale);
 
     // 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,
+    // However, we can't free the memory backing the SkCanvas until the paint finishes,
     // so we grab a local reference here to hold the updater alive until the paint completes.
     RefPtr<LayerTextureUpdater> protector(textureUpdater());
-    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);
+    IntRect paintedOpaqueRect;
+    textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthScale, 1 / heightScale, paintedOpaqueRect, stats);
+
     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.
+            ASSERT(tile); // Did setTexturePriorites get skipped?
+            // FIXME: This should not ever be null.
             if (!tile)
-                CRASH();
+                continue;
+
+            IntRect tileRect = m_tiler->tileBounds(i, j);
 
-            // Use m_updateRect as copyAndClearDirty above moved the existing dirty rect to m_updateRect.
-            const IntRect& dirtyRect = tile->m_updateRect;
+            // Use updateRect as the above loop copied the dirty rect for this frame to updateRect.
+            const IntRect& dirtyRect = tile->updateRect;
             if (dirtyRect.isEmpty())
                 continue;
 
+            // Save what was painted opaque in the tile. Keep the old area if the paint didn't touch it, and didn't paint some
+            // other part of the tile opaque.
+            IntRect tilePaintedRect = intersection(tileRect, paintRect);
+            IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaqueRect);
+            if (!tilePaintedRect.isEmpty()) {
+                IntRect paintInsideTileOpaqueRect = intersection(tile->opaqueRect(), tilePaintedRect);
+                bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRect.contains(paintInsideTileOpaqueRect);
+                bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect.isEmpty() && !tile->opaqueRect().contains(tilePaintedOpaqueRect);
+
+                if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInsideTileOpaqueRect)
+                    tile->setOpaqueRect(tilePaintedOpaqueRect);
+            }
+
+            // sourceRect starts as a full-sized tile with border texels included.
             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);
+            sourceRect.intersect(paintRect);
+
+            tile->updateRect = sourceRect;
 
-            tile->m_updateRect = sourceRect;
             if (sourceRect.isEmpty())
                 continue;
 
-            tile->texture()->prepareRect(sourceRect);
+            tile->texture()->prepareRect(sourceRect, stats);
+            if (occlusion)
+                occlusion->overdrawMetrics().didUpload(WebTransformationMatrix(), sourceRect, tile->opaqueRect());
+
+            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());
+            if (destRect.x() < 0)
+                CRASH();
+            if (destRect.y() < 0)
+                CRASH();
+
+            // Offset from paint rectangle to this tile's dirty rectangle.
+            IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y());
+            if (paintOffset.x() < 0)
+                CRASH();
+            if (paintOffset.y() < 0)
+                CRASH();
+            if (paintOffset.x() + destRect.width() > paintRect.width())
+                CRASH();
+            if (paintOffset.y() + destRect.height() > paintRect.height())
+                CRASH();
+
+            TextureUploader::Parameters upload = { tile->texture(), sourceRect, destRect };
+            if (tile->partialUpdate)
+                queue.appendPartialUpload(upload);
+            else
+                queue.appendFullUpload(upload);
         }
     }
 }
 
-
-void TiledLayerChromium::prepareToUpdate(const IntRect& layerRect)
+void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc)
 {
-    m_skipsDraw = false;
-    m_skipsIdlePaint = false;
-    m_requestedUpdateTilesRect = IntRect();
-    m_paintRect = IntRect();
+    setTexturePrioritiesInRect(priorityCalc, visibleContentRect());
+}
 
+void TiledLayerChromium::setTexturePrioritiesInRect(const CCPriorityCalculator& priorityCalc, const IntRect& visibleContentRect)
+{
     updateBounds();
+    resetUpdateState();
+
+    IntRect prepaintRect = idlePaintRect(visibleContentRect);
+    bool drawsToRoot = !renderTarget()->parent();
+
+    // Minimally create the tiles in the desired pre-paint rect.
+    if (!prepaintRect.isEmpty()) {
+        int left, top, right, bottom;
+        m_tiler->contentRectToTileIndices(prepaintRect, left, top, right, bottom);
+        for (int j = top; j <= bottom; ++j)
+            for (int i = left; i <= right; ++i)
+                if (!tileAt(i, j))
+                    createTile(i, j);
+    }
 
-    if (layerRect.isEmpty() || !m_tiler->numTiles())
-        return;
+    // Create additional textures for double-buffered updates when needed.
+    // These textures must stay alive while the updated textures are incrementally
+    // uploaded, swapped atomically via pushProperties, and finally deleted
+    // after the commit is complete, after which they can be recycled.
+    if (!visibleContentRect.isEmpty()) {
+        int left, top, right, bottom;
+        m_tiler->contentRectToTileIndices(visibleContentRect, 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);
+                // We need an additional texture if the tile needs a buffered-update and it's not a partial update.
+                // FIXME: Decide if partial update should be allowed based on cost
+                // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
+                if (!layerTreeHost() || !layerTreeHost()->bufferedUpdates() || !tileNeedsBufferedUpdate(tile))
+                    continue;
+                if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate()) {
+                    tile->partialUpdate = true;
+                    continue;
+                }
 
-    int left, top, right, bottom;
-    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
+                tile->dirtyRect = m_tiler->tileRect(tile);
+                LayerTextureUpdater::Texture* backBuffer = tile->texture();
+                backBuffer->texture()->setRequestPriority(priorityCalc.priorityFromVisibility(true, drawsToRoot));
+                OwnPtr<CCPrioritizedTexture> frontBuffer = CCPrioritizedTexture::create(backBuffer->texture()->textureManager(),
+                                                                                        backBuffer->texture()->size(),
+                                                                                        backBuffer->texture()->format());
+                // Swap backBuffer into frontBuffer and add it to delete after commit queue.
+                backBuffer->swapTextureWith(frontBuffer);
+                layerTreeHost()->deleteTextureAfterCommit(frontBuffer.release());
+            }
+        }
+    }
 
-    prepareToUpdateTiles(false, left, top, right, bottom);
+    // Now set priorities on 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());
+        IntRect tileRect = m_tiler->tileRect(tile);
+        // FIXME: This indicates the "small animated layer" case. This special case
+        // can be removed soon with better priorities, but for now paint these layers after
+        // 512 pixels of pre-painting. Later we can just pass an animating flag etc. to the
+        // calculator and it can take care of this special case if we still need it.
+        if (visibleContentRect.isEmpty() && !prepaintRect.isEmpty())
+            tile->managedTexture()->setRequestPriority(priorityCalc.priorityFromDistance(512, drawsToRoot));
+        else if (!visibleContentRect.isEmpty())
+            tile->managedTexture()->setRequestPriority(priorityCalc.priorityFromDistance(visibleContentRect, tileRect, drawsToRoot));
+    }
 }
 
-void TiledLayerChromium::prepareToUpdateIdle(const IntRect& layerRect)
+Region TiledLayerChromium::visibleContentOpaqueRegion() const
 {
-    // Abort if we have already prepared a paint or run out of memory.
-    if (m_skipsIdlePaint || !m_paintRect.isEmpty())
-        return;
+    if (m_skipsDraw)
+        return Region();
+    if (opaque())
+        return visibleContentRect();
+    return m_tiler->opaqueRegionInContentRect(visibleContentRect());
+}
 
-    ASSERT(m_tiler);
+void TiledLayerChromium::resetUpdateState()
+{
+    CCLayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end();
+    for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != end; ++iter) {
+        UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+        // FIXME: This should not ever be null.
+        if (!tile)
+            continue;
+        tile->resetUpdateState();
+    }
+}
 
+void TiledLayerChromium::updateContentRect(CCTextureUpdateQueue& queue, const IntRect& contentRect, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
+{
+    m_skipsDraw = false;
+    m_failedUpdate = false;
     updateBounds();
 
-    if (m_tiler->isEmpty())
+    if (m_tiler->hasEmptyBounds())
         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);
+    bool didPaint = false;
+
+    // Visible painting. Only paint visible tiles if the visible rect isn't empty.
+    if (!contentRect.isEmpty()) {
+        int left, top, right, bottom;
+        m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+        markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
+        m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint);
+        if (m_skipsDraw)
+            m_tiler->reset();
+        if (m_skipsDraw || didPaint)
+            return;
+    }
+
+    // If we have already painting everything visible. Do some pre-painting while idle.
+    IntRect idlePaintContentRect = idlePaintRect(contentRect);
+    if (idlePaintContentRect.isEmpty())
+        return;
 
-    // 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)) {
+    m_tiler->contentRectToTileIndices(idlePaintContentRect, 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 (contentRect.isEmpty()) {
+        if (!updateTiles(prepaintLeft, prepaintTop, prepaintRight, prepaintTop, queue, 0, stats, didPaint) || didPaint)
+            return;
+        if (!updateTiles(prepaintLeft, prepaintBottom, prepaintRight, prepaintBottom, queue, 0, stats, didPaint) || didPaint)
+            return;
+        if (!updateTiles(prepaintLeft, prepaintTop, prepaintLeft, prepaintBottom, queue, 0, stats, didPaint) || didPaint)
+            return;
+        updateTiles(prepaintRight, prepaintTop, prepaintRight, prepaintBottom, queue, 0, stats, didPaint);
+        return;
+    }
+
+    int left, top, right, bottom;
+    m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
+
+    // Otherwise, prepaint anything that was occluded but inside the layer's visible region.
+    if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
+        return;
+
+    // Then expand outwards from the visible area until we find a dirty row or column to update.
+    while (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 (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
         if (top > prepaintTop) {
             --top;
-            prepareToUpdateTiles(true, left, top, right, top);
-            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
-                break;
+            if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
         if (left > prepaintLeft) {
             --left;
-            prepareToUpdateTiles(true, left, top, left, bottom);
-            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
-                break;
+            if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
         if (right < prepaintRight) {
             ++right;
-            prepareToUpdateTiles(true, right, top, right, bottom);
-            if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
-                break;
+            if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
     }
 }
 
-bool TiledLayerChromium::needsIdlePaint(const IntRect& layerRect)
+bool TiledLayerChromium::needsIdlePaint(const IntRect& visibleContentRect)
 {
-    if (m_skipsIdlePaint)
+    // Don't trigger more paints if we failed (as we'll just fail again).
+    if (m_failedUpdate)
         return false;
 
-    IntRect idlePaintLayerRect = idlePaintRect(layerRect);
+    if (m_tiler->hasEmptyBounds())
+        return false;
+
+    IntRect idlePaintContentRect = idlePaintRect(visibleContentRect);
+    if (idlePaintContentRect.isEmpty())
+        return false;
 
     int left, top, right, bottom;
-    m_tiler->layerRectToTileIndices(idlePaintLayerRect, 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)))
+            // If the visibleContentRect is empty, then we are painting the outer-most set of tiles only.
+            if (visibleContentRect.isEmpty() && i != left && i != right && j != top && j != bottom)
                 continue;
             UpdatableTile* tile = tileAt(i, j);
-            if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat) || tile->isDirty())
+            ASSERT(tile); // Did setTexturePriorities get skipped?
+            if (!tile)
+                continue;
+
+            bool updated = !tile->updateRect.isEmpty();
+            bool canAcquire = tile->managedTexture()->canAcquireBackingTexture();
+            bool dirty = tile->isDirty() || !tile->managedTexture()->haveBackingTexture();
+            if (!updated && canAcquire && dirty)
                 return true;
         }
     }
     return false;
 }
 
-IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleLayerRect)
+IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleContentRect)
 {
-    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
+    IntRect contentRect(IntPoint::zero(), contentBounds());
+
+    // 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 (visibleContentRect.isEmpty()) {
+        bool isSmallLayer = m_tiler->numTilesX() <= 9 && m_tiler->numTilesY() <= 9 && m_tiler->numTilesX() * m_tiler->numTilesY() <= 9;
+        if ((drawTransformIsAnimating() || screenSpaceTransformIsAnimating()) && isSmallLayer)
+            return contentRect;
+        return IntRect();
+    }
+
+    // FIXME: This can be made a lot larger now! We should increase
+    //        this slowly while insuring it doesn't cause any perf issues.
+    IntRect prepaintRect = visibleContentRect;
     prepaintRect.inflateX(m_tiler->tileSize().width());
-    prepaintRect.inflateY(m_tiler->tileSize().height());
-    prepaintRect.intersect(IntRect(IntPoint::zero(), contentBounds()));
+    prepaintRect.inflateY(m_tiler->tileSize().height() * 2);
+    prepaintRect.intersect(contentRect);
+
     return prepaintRect;
 }