Unreviewed, rolling out r125230 and r125238.
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / TiledLayerChromium.cpp
index 11e9d8d..ccbd58d 100644 (file)
 #include "TiledLayerChromium.h"
 
 #include "GraphicsContext3D.h"
-#include "LayerRendererChromium.h"
-#include "ManagedTexture.h"
 #include "Region.h"
 #include "TextStream.h"
-#include "TraceEvent.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>
@@ -57,27 +56,43 @@ public:
     }
 
     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(); }
-    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()
     {
+        updateRect = IntRect();
+        occluded = false;
+        partialUpdate = false;
+        validForFrame = !isDirty();
+    }
+
+    // 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();
     }
-    // Returns whether the layer was dirty and not updated in the current frame. For tiles that were not culled, the
-    // updateRect holds the area of the tile that was updated. Otherwise, the area that would have been updated.
-    bool isDirtyForCurrentFrame() { return !dirtyRect.isEmpty() && (updateRect.isEmpty() || !updated); }
 
     IntRect dirtyRect;
     IntRect updateRect;
     bool partialUpdate;
-    bool updated;
+    bool validForFrame;
+    bool occluded;
     bool isInUseOnImpl;
 private:
     explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture)
         : partialUpdate(false)
-        , updated(false)
+        , validForFrame(false)
+        , occluded(false)
         , isInUseOnImpl(false)
         , m_texture(texture)
     {
@@ -90,9 +105,8 @@ TiledLayerChromium::TiledLayerChromium()
     : LayerChromium()
     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
     , m_skipsDraw(false)
-    , m_skipsIdlePaint(false)
+    , m_failedUpdate(false)
     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
-    , m_didPaint(false)
     , m_tilingOption(AutoTile)
 {
     m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderTexels);
@@ -156,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)
@@ -216,22 +230,25 @@ void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
         int i = iter->first.first;
         int j = iter->first.second;
         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
+        // FIXME: This should not ever be null.
+        if (!tile)
+            continue;
         tile->isInUseOnImpl = false;
-        if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat)) {
+        if (!tile->managedTexture()->haveBackingTexture()) {
             invalidTiles.append(tile);
             continue;
         }
-        if (tile->isDirtyForCurrentFrame())
+        if (!tile->validForFrame)
             continue;
 
-        tiledLayer->pushTileProperties(i, j, tile->managedTexture()->textureId(), tile->opaqueRect());
+        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;
@@ -243,6 +260,9 @@ 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());
         }
     }
@@ -256,7 +276,11 @@ UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
 
 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
 {
+    createTextureUpdaterIfNeeded();
+
     OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTexture(textureManager())));
+    tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat);
+
     UpdatableTile* addedTile = tile.get();
     m_tiler->addTile(tile.release(), i, j);
 
@@ -273,61 +297,47 @@ UpdatableTile* TiledLayerChromium::createTile(int i, int j)
 
 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
 {
+    float contentsWidthScale = static_cast<float>(contentBounds().width()) / bounds().width();
+    float contentsHeightScale = static_cast<float>(contentBounds().height()) / bounds().height();
     FloatRect scaledDirtyRect(dirtyRect);
-    scaledDirtyRect.scale(contentsScale());
+    scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale);
     IntRect dirty = enclosingIntRect(scaledDirtyRect);
-    invalidateRect(dirty);
+    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)
 {
     updateBounds();
-    if (m_tiler->isEmpty() || layerRect.isEmpty() || m_skipsDraw)
+    if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw)
         return;
 
     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(layerRect);
+        bound.intersect(contentRect);
         tile->dirtyRect.unite(bound);
     }
 }
 
-void TiledLayerChromium::protectTileTextures(const IntRect& layerRect)
-{
-    if (m_tiler->isEmpty() || layerRect.isEmpty())
-        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 || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
-                continue;
-
-            tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat);
-        }
-    }
-}
-
 // Returns true if tile is dirty and only part of it needs to be updated.
 bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
 {
@@ -338,7 +348,7 @@ bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
 // we don't modify textures currently used for drawing by the impl thread.
 bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile)
 {
-    if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
+    if (!tile->managedTexture()->haveBackingTexture())
         return false;
 
     if (!tile->isDirty())
@@ -350,89 +360,114 @@ bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile)
     return true;
 }
 
-void TiledLayerChromium::updateTiles(bool idle, int left, int top, int right, int bottom, CCTextureUpdater& updater, const CCOcclusionTracker* occlusion)
+
+bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats, bool& didPaint)
 {
+    didPaint = false;
     createTextureUpdaterIfNeeded();
 
-    // 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 updateRect set to dirtyRect and dirtyRect cleared. This way if
-    // invalidateRect is invoked during updateLayerRect we don't lose the request.
-    IntRect paintRect;
+    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);
+            ASSERT(tile); // Did setTexturePriorities get skipped?
+            // FIXME: This should not ever be null.
             if (!tile)
-                tile = createTile(i, j);
-
-            // Temporary diagnostic crash
-            if (!m_tiler)
-                CRASH();
-
-            if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat)) {
-                // Sets the dirty rect to a full-sized tile with border texels.
-                tile->dirtyRect = m_tiler->tileRect(tile);
+                continue;
+            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();
             }
+        }
+    }
 
-            // When not idle painting, if the visible region of the tile is occluded, don't reserve a texture or update the tile.
-            // If any part of the tile is visible, then we need to update it so the tile is pushed to the impl thread.
-            if (!idle && occlusion) {
-                IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), visibleLayerRect());
-                if (occlusion->occluded(this, visibleTileRect)) {
-                    ASSERT(!tile->updated);
-                    continue;
-                }
-            }
+    if (!succeeded)
+        return;
 
-            // We come through this function multiple times during a commit, and updated should be true if the tile is not culled
-            // any single time through the function.
-            tile->updated = true;
+    // FIXME: Remove the loop and just pass the count!
+    for (int i = 0; i < occludedTileCount; i++)
+        occlusion->overdrawMetrics().didCullTileForUpload();
+}
 
-            if (layerTreeHost() && layerTreeHost()->bufferedUpdates() && tileNeedsBufferedUpdate(tile)) {
-                // FIXME: Decide if partial update should be allowed based on cost
-                // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
-                if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate())
-                    tile->partialUpdate = true;
-                else {
-                    layerTreeHost()->deleteTextureAfterCommit(tile->managedTexture()->steal());
-                    // Sets the dirty rect to a full-sized tile with border texels.
-                    tile->dirtyRect = m_tiler->tileRect(tile);
-                }
-            }
+bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions)
+{
+    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->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
-                m_skipsIdlePaint = true;
-                if (!idle) {
-                    m_skipsDraw = true;
-                    m_tiler->reset();
-                }
-                return;
-            }
+            // Ensure the entire tile is dirty if we don't have the texture.
+            if (!tile->managedTexture()->haveBackingTexture())
+                tile->dirtyRect = m_tiler->tileRect(tile);
 
-            paintRect.unite(tile->dirtyRect);
+            // 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;
+
+            if (!tile->managedTexture()->canAcquireBackingTexture())
+                return false;
         }
     }
+    return true;
+}
 
-    // For tiles that were not culled, we are going to update the area currently marked as dirty. So
-    // clear that dirty area and mark it for update instead.
+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)
-                CRASH();
-            if (tile->updated)
-                tile->copyAndClearDirty();
-            else if (!idle && occlusion && tile->isDirty())
-                occlusion->overdrawMetrics().didCullTileForUpload();
+                continue;
+            if (tile->occluded && !ignoreOcclusions)
+                continue;
+            paintRect.unite(tile->dirtyRect);
+            tile->markForUpdate();
         }
     }
+    return paintRect;
+}
 
-    if (paintRect.isEmpty())
-        return;
-
-    if (occlusion)
-        occlusion->overdrawMetrics().didPaint(paintRect);
-
+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());
@@ -441,22 +476,22 @@ void TiledLayerChromium::updateTiles(bool idle, int left, int top, int right, in
 
     // 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 paintedOpaqueRect;
-    textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), contentsScale(), paintedOpaqueRect);
-    m_didPaint = true;
+    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);
+            ASSERT(tile); // Did setTexturePriorites get skipped?
+            // FIXME: This should not ever be null.
+            if (!tile)
+                continue;
 
             IntRect tileRect = m_tiler->tileBounds(i, j);
 
-            if (!tile->updated)
-                continue;
-
             // Use updateRect as the above loop copied the dirty rect for this frame to updateRect.
             const IntRect& dirtyRect = tile->updateRect;
             if (dirtyRect.isEmpty())
@@ -487,7 +522,7 @@ void TiledLayerChromium::updateTiles(bool idle, int left, int top, int right, in
             if (sourceRect.isEmpty())
                 continue;
 
-            tile->texture()->prepareRect(sourceRect);
+            tile->texture()->prepareRect(sourceRect, stats);
             if (occlusion)
                 occlusion->overdrawMetrics().didUpload(WebTransformationMatrix(), sourceRect, tile->opaqueRect());
 
@@ -511,39 +546,86 @@ void TiledLayerChromium::updateTiles(bool idle, int left, int top, int right, in
             if (paintOffset.y() + destRect.height() > paintRect.height())
                 CRASH();
 
+            TextureUploader::Parameters upload = { tile->texture(), sourceRect, destRect };
             if (tile->partialUpdate)
-                updater.appendPartialUpdate(tile->texture(), sourceRect, destRect);
+                queue.appendPartialUpload(upload);
             else
-                updater.appendUpdate(tile->texture(), sourceRect, destRect);
+                queue.appendFullUpload(upload);
         }
     }
 }
 
-void TiledLayerChromium::reserveTextures()
+void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc)
 {
-    updateBounds();
+    setTexturePrioritiesInRect(priorityCalc, visibleContentRect());
+}
 
-    const IntRect& layerRect = visibleLayerRect();
-    if (layerRect.isEmpty() || m_tiler->hasEmptyBounds())
-        return;
+void TiledLayerChromium::setTexturePrioritiesInRect(const CCPriorityCalculator& priorityCalc, const IntRect& visibleContentRect)
+{
+    updateBounds();
+    resetUpdateState();
 
-    int left, top, right, bottom;
-    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
+    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);
+    }
 
-    createTextureUpdaterIfNeeded();
-    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);
+    // 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;
+                }
 
-            if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
                 tile->dirtyRect = m_tiler->tileRect(tile);
-
-            if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat))
-                return;
+                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());
+            }
         }
     }
+
+    // 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));
+    }
 }
 
 Region TiledLayerChromium::visibleContentOpaqueRegion() const
@@ -551,8 +633,8 @@ Region TiledLayerChromium::visibleContentOpaqueRegion() const
     if (m_skipsDraw)
         return Region();
     if (opaque())
-        return visibleLayerRect();
-    return m_tiler->opaqueRegionInLayerRect(visibleLayerRect());
+        return visibleContentRect();
+    return m_tiler->opaqueRegionInContentRect(visibleContentRect());
 }
 
 void TiledLayerChromium::resetUpdateState()
@@ -560,155 +642,145 @@ 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());
-        tile->updateRect = IntRect();
-        tile->partialUpdate = false;
-        tile->updated = false;
+        // FIXME: This should not ever be null.
+        if (!tile)
+            continue;
+        tile->resetUpdateState();
     }
 }
 
-void TiledLayerChromium::updateLayerRect(CCTextureUpdater& updater, const IntRect& layerRect, const CCOcclusionTracker* occlusion)
+void TiledLayerChromium::updateContentRect(CCTextureUpdateQueue& queue, const IntRect& contentRect, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
 {
     m_skipsDraw = false;
-    m_skipsIdlePaint = false;
-    m_didPaint = false;
-
-    updateBounds();
-
-    resetUpdateState();
-
-    if (layerRect.isEmpty() || m_tiler->hasEmptyBounds())
-        return;
-
-    int left, top, right, bottom;
-    m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
-
-    updateTiles(false, left, top, right, bottom, updater, occlusion);
-}
-
-void TiledLayerChromium::idleUpdateLayerRect(CCTextureUpdater& updater, const IntRect& layerRect, const CCOcclusionTracker* occlusion)
-{
-    // Abort if we have already painted or run out of memory.
-    if (m_skipsIdlePaint || m_didPaint)
-        return;
-
-    ASSERT(m_tiler);
-
+    m_failedUpdate = false;
     updateBounds();
 
     if (m_tiler->hasEmptyBounds())
         return;
 
-    IntRect idlePaintLayerRect = idlePaintRect(layerRect);
-    if (idlePaintLayerRect.isEmpty())
-        return;
+    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;
+    }
 
-    // Protect any textures in the pre-paint area, as we would steal them from other layers
-    // over time anyhow. This ensures we don't lose tiles in the first rounds of idle painting
-    // that we have already painted.
-    protectTileTextures(idlePaintLayerRect);
+    // If we have already painting everything visible. Do some pre-painting while idle.
+    IntRect idlePaintContentRect = idlePaintRect(contentRect);
+    if (idlePaintContentRect.isEmpty())
+        return;
 
     int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
-    m_tiler->layerRectToTileIndices(idlePaintLayerRect, prepaintLeft, prepaintTop, prepaintRight, 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 (layerRect.isEmpty()) {
-        updateTiles(true, prepaintLeft, prepaintTop, prepaintRight, prepaintTop, updater, 0);
-        if (m_didPaint || m_skipsIdlePaint)
+    if (contentRect.isEmpty()) {
+        if (!updateTiles(prepaintLeft, prepaintTop, prepaintRight, prepaintTop, queue, 0, stats, didPaint) || didPaint)
             return;
-        updateTiles(true, prepaintLeft, prepaintBottom, prepaintRight, prepaintBottom, updater, 0);
-        if (m_didPaint || m_skipsIdlePaint)
+        if (!updateTiles(prepaintLeft, prepaintBottom, prepaintRight, prepaintBottom, queue, 0, stats, didPaint) || didPaint)
             return;
-        updateTiles(true, prepaintLeft, prepaintTop, prepaintLeft, prepaintBottom, updater, 0);
-        if (m_didPaint || m_skipsIdlePaint)
+        if (!updateTiles(prepaintLeft, prepaintTop, prepaintLeft, prepaintBottom, queue, 0, stats, didPaint) || didPaint)
             return;
-        updateTiles(true, prepaintRight, prepaintTop, prepaintRight, prepaintBottom, updater, 0);
+        updateTiles(prepaintRight, prepaintTop, prepaintRight, prepaintBottom, queue, 0, stats, didPaint);
         return;
     }
 
     int left, top, right, bottom;
-    m_tiler->layerRectToTileIndices(layerRect, 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.
-    updateTiles(true, left, top, right, bottom, updater, 0);
-    if (m_didPaint || m_skipsIdlePaint)
+    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 (!m_skipsIdlePaint && (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom)) {
+    while (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom) {
         if (bottom < prepaintBottom) {
             ++bottom;
-            updateTiles(true, left, bottom, right, bottom, updater, 0);
-            if (m_didPaint || m_skipsIdlePaint)
-                break;
+            if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
         if (top > prepaintTop) {
             --top;
-            updateTiles(true, left, top, right, top, updater, 0);
-            if (m_didPaint || m_skipsIdlePaint)
-                break;
+            if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
         if (left > prepaintLeft) {
             --left;
-            updateTiles(true, left, top, left, bottom, updater, 0);
-            if (m_didPaint || m_skipsIdlePaint)
-                break;
+            if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint)
+                return;
         }
         if (right < prepaintRight) {
             ++right;
-            updateTiles(true, right, top, right, bottom, updater, 0);
-            if (m_didPaint || 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;
 
     if (m_tiler->hasEmptyBounds())
         return false;
 
-    IntRect idlePaintLayerRect = idlePaintRect(layerRect);
-    if (idlePaintLayerRect.isEmpty())
+    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 the layerRect is empty, then we are painting the outer-most set of tiles only.
-            if (layerRect.isEmpty() && i != left && i != right && j != top && j != bottom)
+            // 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 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 (visibleLayerRect.isEmpty()) {
+    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 IntRect(IntPoint(), contentBounds());
+            return contentRect;
         return IntRect();
     }
 
-    IntRect prepaintRect = visibleLayerRect;
-    // FIXME: This can be made a lot larger if we can:
-    // - reserve memory at a lower priority than for visible content
-    // - only reserve idle paint tiles up to a memory reclaim threshold and
-    // - insure we play nicely with other layers
+    // 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;
 }