Index: cc/tiled_layer.cc |
diff --git a/cc/tiled_layer.cc b/cc/tiled_layer.cc |
index 1f968ad4bb1820562fdc697e508cd824058ac052..aa487fff3abf57bf22362d2eacd5a4bfd32e11d2 100644 |
--- a/cc/tiled_layer.cc |
+++ b/cc/tiled_layer.cc |
@@ -19,6 +19,16 @@ using WebKit::WebTransformationMatrix; |
namespace cc { |
+// Maximum predictive expansion of the visible area. |
+static const int maxPredictiveTilesCount = 2; |
+ |
+// Number of rows/columns of tiles to pre-paint. |
+// We should increase these further as all textures are |
+// prioritized and we insure performance doesn't suffer. |
+static const int prepaintRows = 4; |
+static const int prepaintColumns = 2; |
+ |
+ |
class UpdatableTile : public LayerTilingData::Tile { |
public: |
static scoped_ptr<UpdatableTile> create(scoped_ptr<LayerTextureUpdater::Texture> texture) |
@@ -566,6 +576,7 @@ void TiledLayer::setTexturePriorities(const PriorityCalculator& priorityCalc) |
{ |
updateBounds(); |
resetUpdateState(); |
+ updateScrollPrediction(); |
if (m_tiler->hasEmptyBounds()) |
return; |
@@ -635,7 +646,7 @@ void TiledLayer::setTexturePriorities(const PriorityCalculator& priorityCalc) |
if (!tile) |
continue; |
IntRect tileRect = m_tiler->tileRect(tile); |
- setPriorityForTexture(visibleContentRect(), tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture()); |
+ setPriorityForTexture(m_predictedVisibleRect, tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture()); |
} |
} |
@@ -663,6 +674,46 @@ void TiledLayer::resetUpdateState() |
} |
} |
+namespace { |
+IntRect expandRectByDelta(IntRect rect, IntSize delta) { |
+ int width = rect.width() + abs(delta.width()); |
+ int height = rect.height() + abs(delta.height()); |
+ int x = rect.x() + ((delta.width() < 0) ? delta.width() : 0); |
+ int y = rect.y() + ((delta.height() < 0) ? delta.height() : 0); |
+ return IntRect(x, y, width, height); |
+} |
+} |
+ |
+void TiledLayer::updateScrollPrediction() |
+{ |
+ // This scroll prediction is very primitive and should be replaced by a |
+ // a recursive calculation on all layers which uses actual scroll/animation |
+ // velocities. To insure this doesn't miss-predict, we only use it to predict |
+ // the visibleRect if: |
+ // - contentBounds() hasn't changed. |
+ // - visibleRect.size() hasn't changed. |
+ // These two conditions prevent rotations, scales, pinch-zooms etc. where |
+ // the prediction would be incorrect. |
+ IntSize delta = visibleContentRect().center() - m_previousVisibleRect.center(); |
+ m_predictedScroll = -delta; |
+ m_predictedVisibleRect = visibleContentRect(); |
+ if (m_previousContentBounds == contentBounds() && m_previousVisibleRect.size() == visibleContentRect().size()) { |
+ // Only expand the visible rect in the major scroll direction, to prevent |
+ // massive paints due to diagonal scrolls. |
+ IntSize majorScrollDelta = (abs(delta.width()) > abs(delta.height())) ? IntSize(delta.width(), 0) : IntSize(0, delta.height()); |
+ m_predictedVisibleRect = expandRectByDelta(visibleContentRect(), majorScrollDelta); |
+ |
+ // Bound the prediction to prevent unbounded paints, and clamp to content bounds. |
+ IntRect bound = visibleContentRect(); |
+ bound.inflateX(m_tiler->tileSize().width() * maxPredictiveTilesCount); |
+ bound.inflateY(m_tiler->tileSize().height() * maxPredictiveTilesCount); |
+ bound.intersect(IntRect(IntPoint::zero(), contentBounds())); |
+ m_predictedVisibleRect.intersect(bound); |
+ } |
+ m_previousContentBounds = contentBounds(); |
+ m_previousVisibleRect = visibleContentRect(); |
+} |
+ |
void TiledLayer::update(TextureUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats) |
{ |
DCHECK(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped? |
@@ -686,12 +737,12 @@ void TiledLayer::update(TextureUpdateQueue& queue, const OcclusionTracker* occlu |
m_failedUpdate = false; |
} |
- if (visibleContentRect().isEmpty()) |
+ if (m_predictedVisibleRect.isEmpty()) |
return; |
// Visible painting. First occlude visible tiles and paint the non-occluded tiles. |
int left, top, right, bottom; |
- m_tiler->contentRectToTileIndices(visibleContentRect(), left, top, right, bottom); |
+ m_tiler->contentRectToTileIndices(m_predictedVisibleRect, left, top, right, bottom); |
markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion); |
m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint); |
if (m_skipsDraw) |
@@ -711,27 +762,42 @@ void TiledLayer::update(TextureUpdateQueue& queue, const OcclusionTracker* occlu |
int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom; |
m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom); |
- // 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; |
- if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint) |
- return; |
+ // Then expand outwards one row/column at a time until we find a dirty row/column |
+ // to update. Increment along the major and minor scroll directions first. |
+ IntSize delta = -m_predictedScroll; |
+ delta = IntSize(delta.width() == 0 ? 1 : delta.width(), |
+ delta.height() == 0 ? 1 : delta.height()); |
+ IntSize majorDelta = (abs(delta.width()) > abs(delta.height())) ? IntSize(delta.width(), 0) : IntSize(0, delta.height()); |
+ IntSize minorDelta = (abs(delta.width()) <= abs(delta.height())) ? IntSize(delta.width(), 0) : IntSize(0, delta.height()); |
+ IntSize deltas[4] = {majorDelta, minorDelta, -majorDelta, -minorDelta}; |
+ for(int i = 0; i < 4; i++) { |
+ if (deltas[i].height() > 0) { |
+ while (bottom < prepaintBottom) { |
+ ++bottom; |
+ if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint) |
+ return; |
+ } |
} |
- if (top > prepaintTop) { |
- --top; |
- if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint) |
- return; |
+ if (deltas[i].height() < 0) { |
+ while (top > prepaintTop) { |
+ --top; |
+ if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint) |
+ return; |
+ } |
} |
- if (left > prepaintLeft) { |
- --left; |
- if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint) |
- return; |
+ if (deltas[i].width() < 0) { |
+ while (left > prepaintLeft) { |
+ --left; |
+ if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint) |
+ return; |
+ } |
} |
- if (right < prepaintRight) { |
- ++right; |
- if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint) |
- return; |
+ if (deltas[i].width() > 0) { |
+ while (right < prepaintRight) { |
+ ++right; |
+ if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint) |
+ return; |
+ } |
} |
} |
} |
@@ -772,11 +838,9 @@ IntRect TiledLayer::idlePaintRect() |
if (visibleContentRect().isEmpty()) |
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() * 2); |
+ prepaintRect.inflateX(m_tiler->tileSize().width() * prepaintColumns); |
+ prepaintRect.inflateY(m_tiler->tileSize().height() * prepaintRows); |
IntRect contentRect(IntPoint::zero(), contentBounds()); |
prepaintRect.intersect(contentRect); |