WebKit Roll 139512:139548
[chromium-blink-merge.git] / cc / tiled_layer.cc
blobd814adaffc5b7cb445e9357442acf543f2d69a99
1 // Copyright 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/tiled_layer.h"
7 #include "base/auto_reset.h"
8 #include "base/basictypes.h"
9 #include "build/build_config.h"
10 #include "cc/layer_impl.h"
11 #include "cc/layer_tree_host.h"
12 #include "cc/layer_updater.h"
13 #include "cc/overdraw_metrics.h"
14 #include "cc/prioritized_resource.h"
15 #include "cc/priority_calculator.h"
16 #include "cc/tiled_layer_impl.h"
17 #include "third_party/khronos/GLES2/gl2.h"
18 #include "ui/gfx/rect_conversions.h"
20 namespace cc {
22 // Maximum predictive expansion of the visible area.
23 static const int maxPredictiveTilesCount = 2;
25 // Number of rows/columns of tiles to pre-paint.
26 // We should increase these further as all textures are
27 // prioritized and we insure performance doesn't suffer.
28 static const int prepaintRows = 4;
29 static const int prepaintColumns = 2;
32 class UpdatableTile : public LayerTilingData::Tile {
33 public:
34 static scoped_ptr<UpdatableTile> create(scoped_ptr<LayerUpdater::Resource> updaterResource)
36 return make_scoped_ptr(new UpdatableTile(updaterResource.Pass()));
39 LayerUpdater::Resource* updaterResource() { return m_updaterResource.get(); }
40 PrioritizedResource* managedResource() { return m_updaterResource->texture(); }
42 bool isDirty() const { return !dirtyRect.IsEmpty(); }
44 // Reset update state for the current frame. This should occur before painting
45 // for all layers. Since painting one layer can invalidate another layer
46 // after it has already painted, mark all non-dirty tiles as valid before painting
47 // such that invalidations during painting won't prevent them from being pushed.
48 void resetUpdateState()
50 updateRect = gfx::Rect();
51 occluded = false;
52 partialUpdate = false;
53 validForFrame = !isDirty();
56 // This promises to update the tile and therefore also guarantees the tile
57 // will be valid for this frame. dirtyRect is copied into updateRect so
58 // we can continue to track re-entrant invalidations that occur during painting.
59 void markForUpdate()
61 validForFrame = true;
62 updateRect = dirtyRect;
63 dirtyRect = gfx::Rect();
66 gfx::Rect dirtyRect;
67 gfx::Rect updateRect;
68 bool partialUpdate;
69 bool validForFrame;
70 bool occluded;
72 private:
73 explicit UpdatableTile(scoped_ptr<LayerUpdater::Resource> updaterResource)
74 : partialUpdate(false)
75 , validForFrame(false)
76 , occluded(false)
77 , m_updaterResource(updaterResource.Pass())
81 scoped_ptr<LayerUpdater::Resource> m_updaterResource;
83 DISALLOW_COPY_AND_ASSIGN(UpdatableTile);
86 TiledLayer::TiledLayer()
87 : ContentsScalingLayer()
88 , m_textureFormat(GL_INVALID_ENUM)
89 , m_skipsDraw(false)
90 , m_failedUpdate(false)
91 , m_tilingOption(AutoTile)
93 m_tiler = LayerTilingData::create(gfx::Size(), LayerTilingData::HasBorderTexels);
96 TiledLayer::~TiledLayer()
100 scoped_ptr<LayerImpl> TiledLayer::createLayerImpl(LayerTreeImpl* treeImpl)
102 return TiledLayerImpl::create(treeImpl, id()).PassAs<LayerImpl>();
105 void TiledLayer::updateTileSizeAndTilingOption()
107 DCHECK(layerTreeHost());
109 gfx::Size defaultTileSize = layerTreeHost()->settings().defaultTileSize;
110 gfx::Size maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledLayerSize;
111 int layerWidth = contentBounds().width();
112 int layerHeight = contentBounds().height();
114 gfx::Size tileSize(std::min(defaultTileSize.width(), layerWidth), std::min(defaultTileSize.height(), layerHeight));
116 // Tile if both dimensions large, or any one dimension large and the other
117 // extends into a second tile but the total layer area isn't larger than that
118 // of the largest possible untiled layer. This heuristic allows for long skinny layers
119 // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but still avoids
120 // creating very large tiles.
121 bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || layerHeight > maxUntiledLayerSize.height();
122 bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || layerHeight <= defaultTileSize.height())
123 && (layerWidth * layerHeight) <= (maxUntiledLayerSize.width() * maxUntiledLayerSize.height());
124 bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
126 bool isTiled;
127 if (m_tilingOption == AlwaysTile)
128 isTiled = true;
129 else if (m_tilingOption == NeverTile)
130 isTiled = false;
131 else
132 isTiled = autoTiled;
134 gfx::Size requestedSize = isTiled ? tileSize : contentBounds();
135 const int maxSize = layerTreeHost()->rendererCapabilities().maxTextureSize;
136 requestedSize.ClampToMax(gfx::Size(maxSize, maxSize));
137 setTileSize(requestedSize);
140 void TiledLayer::updateBounds()
142 gfx::Size oldBounds = m_tiler->bounds();
143 gfx::Size newBounds = contentBounds();
144 if (oldBounds == newBounds)
145 return;
146 m_tiler->setBounds(newBounds);
148 // Invalidate any areas that the new bounds exposes.
149 Region oldRegion = gfx::Rect(gfx::Point(), oldBounds);
150 Region newRegion = gfx::Rect(gfx::Point(), newBounds);
151 newRegion.Subtract(oldRegion);
152 for (Region::Iterator newRects(newRegion); newRects.has_rect(); newRects.next())
153 invalidateContentRect(newRects.rect());
156 void TiledLayer::setTileSize(const gfx::Size& size)
158 m_tiler->setTileSize(size);
161 void TiledLayer::setBorderTexelOption(LayerTilingData::BorderTexelOption borderTexelOption)
163 m_tiler->setBorderTexelOption(borderTexelOption);
166 bool TiledLayer::drawsContent() const
168 if (!ContentsScalingLayer::drawsContent())
169 return false;
171 bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() > 1;
172 if (m_tilingOption == NeverTile && hasMoreThanOneTile)
173 return false;
175 return true;
178 void TiledLayer::setTilingOption(TilingOption tilingOption)
180 m_tilingOption = tilingOption;
183 void TiledLayer::setIsMask(bool isMask)
185 setTilingOption(isMask ? NeverTile : AutoTile);
188 void TiledLayer::pushPropertiesTo(LayerImpl* layer)
190 ContentsScalingLayer::pushPropertiesTo(layer);
192 TiledLayerImpl* tiledLayer = static_cast<TiledLayerImpl*>(layer);
194 tiledLayer->setSkipsDraw(m_skipsDraw);
195 tiledLayer->setTilingData(*m_tiler);
196 std::vector<UpdatableTile*> invalidTiles;
198 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
199 int i = iter->first.first;
200 int j = iter->first.second;
201 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
202 // FIXME: This should not ever be null.
203 if (!tile)
204 continue;
206 if (!tile->managedResource()->haveBackingTexture()) {
207 // Evicted tiles get deleted from both layers
208 invalidTiles.push_back(tile);
209 continue;
212 if (!tile->validForFrame) {
213 // Invalidated tiles are set so they can get different debug colors.
214 tiledLayer->pushInvalidTile(i, j);
215 continue;
218 tiledLayer->pushTileProperties(i, j, tile->managedResource()->resourceId(), tile->opaqueRect(), tile->managedResource()->contentsSwizzled());
220 for (std::vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter)
221 m_tiler->takeTile((*iter)->i(), (*iter)->j());
224 bool TiledLayer::blocksPendingCommit() const
226 return true;
229 PrioritizedResourceManager* TiledLayer::resourceManager() const
231 if (!layerTreeHost())
232 return 0;
233 return layerTreeHost()->contentsTextureManager();
236 const PrioritizedResource* TiledLayer::resourceAtForTesting(int i, int j) const
238 UpdatableTile* tile = tileAt(i, j);
239 if (!tile)
240 return 0;
241 return tile->managedResource();
244 void TiledLayer::setLayerTreeHost(LayerTreeHost* host)
246 if (host && host != layerTreeHost()) {
247 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
248 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
249 // FIXME: This should not ever be null.
250 if (!tile)
251 continue;
252 tile->managedResource()->setTextureManager(host->contentsTextureManager());
255 ContentsScalingLayer::setLayerTreeHost(host);
258 UpdatableTile* TiledLayer::tileAt(int i, int j) const
260 return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
263 UpdatableTile* TiledLayer::createTile(int i, int j)
265 createUpdaterIfNeeded();
267 scoped_ptr<UpdatableTile> tile(UpdatableTile::create(updater()->createResource(resourceManager())));
268 tile->managedResource()->setDimensions(m_tiler->tileSize(), m_textureFormat);
270 UpdatableTile* addedTile = tile.get();
271 m_tiler->addTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
273 addedTile->dirtyRect = m_tiler->tileRect(addedTile);
275 // Temporary diagnostic crash.
276 CHECK(addedTile);
277 CHECK(tileAt(i, j));
279 return addedTile;
282 void TiledLayer::setNeedsDisplayRect(const gfx::RectF& dirtyRect)
284 invalidateContentRect(layerRectToContentRect(dirtyRect));
285 ContentsScalingLayer::setNeedsDisplayRect(dirtyRect);
288 void TiledLayer::invalidateContentRect(const gfx::Rect& contentRect)
290 updateBounds();
291 if (m_tiler->isEmpty() || contentRect.IsEmpty() || m_skipsDraw)
292 return;
294 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
295 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
296 DCHECK(tile);
297 // FIXME: This should not ever be null.
298 if (!tile)
299 continue;
300 gfx::Rect bound = m_tiler->tileRect(tile);
301 bound.Intersect(contentRect);
302 tile->dirtyRect.Union(bound);
306 // Returns true if tile is dirty and only part of it needs to be updated.
307 bool TiledLayer::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
309 return !tile->dirtyRect.Contains(m_tiler->tileRect(tile)) && tile->managedResource()->haveBackingTexture();
312 bool TiledLayer::updateTiles(int left, int top, int right, int bottom, ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats, bool& didPaint)
314 didPaint = false;
315 createUpdaterIfNeeded();
317 bool ignoreOcclusions = !occlusion;
318 if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) {
319 m_failedUpdate = true;
320 return false;
323 gfx::Rect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclusions);
325 if (occlusion)
326 occlusion->overdrawMetrics().didPaint(paintRect);
328 if (paintRect.IsEmpty())
329 return true;
331 didPaint = true;
332 updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, stats);
333 return true;
336 void TiledLayer::markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const OcclusionTracker* occlusion)
338 // There is some difficult dependancies between occlusions, recording occlusion metrics
339 // and requesting memory so those are encapsulated in this function:
340 // - We only want to call requestLate on unoccluded textures (to preserve
341 // memory for other layers when near OOM).
342 // - We only want to record occlusion metrics if all memory requests succeed.
344 int occludedTileCount = 0;
345 bool succeeded = true;
346 for (int j = top; j <= bottom; ++j) {
347 for (int i = left; i <= right; ++i) {
348 UpdatableTile* tile = tileAt(i, j);
349 DCHECK(tile); // Did setTexturePriorities get skipped?
350 // FIXME: This should not ever be null.
351 if (!tile)
352 continue;
353 DCHECK(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occlusion pass?
354 gfx::Rect visibleTileRect = gfx::IntersectRects(m_tiler->tileBounds(i, j), visibleContentRect());
355 if (occlusion && occlusion->occluded(renderTarget(), visibleTileRect, drawTransform(), drawTransformIsAnimating(), drawableContentRect())) {
356 tile->occluded = true;
357 occludedTileCount++;
358 } else {
359 succeeded &= tile->managedResource()->requestLate();
364 if (!succeeded)
365 return;
366 if (occlusion)
367 occlusion->overdrawMetrics().didCullTilesForUpload(occludedTileCount);
370 bool TiledLayer::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions)
372 for (int j = top; j <= bottom; ++j) {
373 for (int i = left; i <= right; ++i) {
374 UpdatableTile* tile = tileAt(i, j);
375 DCHECK(tile); // Did setTexturePriorites get skipped?
376 // FIXME: This should not ever be null.
377 if (!tile)
378 continue;
380 // Ensure the entire tile is dirty if we don't have the texture.
381 if (!tile->managedResource()->haveBackingTexture())
382 tile->dirtyRect = m_tiler->tileRect(tile);
384 // If using occlusion and the visible region of the tile is occluded,
385 // don't reserve a texture or update the tile.
386 if (tile->occluded && !ignoreOcclusions)
387 continue;
389 if (!tile->managedResource()->canAcquireBackingTexture())
390 return false;
393 return true;
396 gfx::Rect TiledLayer::markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions)
398 gfx::Rect paintRect;
399 for (int j = top; j <= bottom; ++j) {
400 for (int i = left; i <= right; ++i) {
401 UpdatableTile* tile = tileAt(i, j);
402 DCHECK(tile); // Did setTexturePriorites get skipped?
403 // FIXME: This should not ever be null.
404 if (!tile)
405 continue;
406 if (tile->occluded && !ignoreOcclusions)
407 continue;
408 // FIXME: Decide if partial update should be allowed based on cost
409 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
410 if (tile->isDirty() && layerTreeHost() && layerTreeHost()->bufferedUpdates()) {
411 // If we get a partial update, we use the same texture, otherwise return the
412 // current texture backing, so we don't update visible textures non-atomically.
413 // If the current backing is in-use, it won't be deleted until after the commit
414 // as the texture manager will not allow deletion or recycling of in-use textures.
415 if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate())
416 tile->partialUpdate = true;
417 else {
418 tile->dirtyRect = m_tiler->tileRect(tile);
419 tile->managedResource()->returnBackingTexture();
423 paintRect.Union(tile->dirtyRect);
424 tile->markForUpdate();
427 return paintRect;
430 void TiledLayer::updateTileTextures(const gfx::Rect& paintRect, int left, int top, int right, int bottom, ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats)
432 // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space.
433 float widthScale = bounds().width() / static_cast<float>(contentBounds().width());
434 float heightScale = bounds().height() / static_cast<float>(contentBounds().height());
435 m_updateRect = gfx::ScaleRect(paintRect, widthScale, heightScale);
437 // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
438 // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
439 // However, we can't free the memory backing the SkCanvas until the paint finishes,
440 // so we grab a local reference here to hold the updater alive until the paint completes.
441 scoped_refptr<LayerUpdater> protector(updater());
442 gfx::Rect paintedOpaqueRect;
443 updater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthScale, 1 / heightScale, paintedOpaqueRect, stats);
445 for (int j = top; j <= bottom; ++j) {
446 for (int i = left; i <= right; ++i) {
447 UpdatableTile* tile = tileAt(i, j);
448 DCHECK(tile); // Did setTexturePriorites get skipped?
449 // FIXME: This should not ever be null.
450 if (!tile)
451 continue;
453 gfx::Rect tileRect = m_tiler->tileBounds(i, j);
455 // Use updateRect as the above loop copied the dirty rect for this frame to updateRect.
456 const gfx::Rect& dirtyRect = tile->updateRect;
457 if (dirtyRect.IsEmpty())
458 continue;
460 // Save what was painted opaque in the tile. Keep the old area if the paint didn't touch it, and didn't paint some
461 // other part of the tile opaque.
462 gfx::Rect tilePaintedRect = gfx::IntersectRects(tileRect, paintRect);
463 gfx::Rect tilePaintedOpaqueRect = gfx::IntersectRects(tileRect, paintedOpaqueRect);
464 if (!tilePaintedRect.IsEmpty()) {
465 gfx::Rect paintInsideTileOpaqueRect = gfx::IntersectRects(tile->opaqueRect(), tilePaintedRect);
466 bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRect.Contains(paintInsideTileOpaqueRect);
467 bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect.IsEmpty() && !tile->opaqueRect().Contains(tilePaintedOpaqueRect);
469 if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInsideTileOpaqueRect)
470 tile->setOpaqueRect(tilePaintedOpaqueRect);
473 // sourceRect starts as a full-sized tile with border texels included.
474 gfx::Rect sourceRect = m_tiler->tileRect(tile);
475 sourceRect.Intersect(dirtyRect);
476 // Paint rect not guaranteed to line up on tile boundaries, so
477 // make sure that sourceRect doesn't extend outside of it.
478 sourceRect.Intersect(paintRect);
480 tile->updateRect = sourceRect;
482 if (sourceRect.IsEmpty())
483 continue;
485 const gfx::Point anchor = m_tiler->tileRect(tile).origin();
487 // Calculate tile-space rectangle to upload into.
488 gfx::Vector2d destOffset = sourceRect.origin() - anchor;
489 CHECK(destOffset.x() >= 0);
490 CHECK(destOffset.y() >= 0);
492 // Offset from paint rectangle to this tile's dirty rectangle.
493 gfx::Vector2d paintOffset = sourceRect.origin() - paintRect.origin();
494 CHECK(paintOffset.x() >= 0);
495 CHECK(paintOffset.y() >= 0);
496 CHECK(paintOffset.x() + sourceRect.width() <= paintRect.width());
497 CHECK(paintOffset.y() + sourceRect.height() <= paintRect.height());
499 tile->updaterResource()->update(queue, sourceRect, destOffset, tile->partialUpdate, stats);
500 if (occlusion)
501 occlusion->overdrawMetrics().didUpload(gfx::Transform(), sourceRect, tile->opaqueRect());
507 // This picks a small animated layer to be anything less than one viewport. This
508 // is specifically for page transitions which are viewport-sized layers. The extra
509 // tile of padding is due to these layers being slightly larger than the viewport
510 // in some cases.
511 bool TiledLayer::isSmallAnimatedLayer() const
513 if (!drawTransformIsAnimating() && !screenSpaceTransformIsAnimating())
514 return false;
515 gfx::Size viewportSize = layerTreeHost() ? layerTreeHost()->deviceViewportSize() : gfx::Size();
516 gfx::Rect contentRect(gfx::Point(), contentBounds());
517 return contentRect.width() <= viewportSize.width() + m_tiler->tileSize().width()
518 && contentRect.height() <= viewportSize.height() + m_tiler->tileSize().height();
521 namespace {
522 // FIXME: Remove this and make this based on distance once distance can be calculated
523 // for offscreen layers. For now, prioritize all small animated layers after 512
524 // pixels of pre-painting.
525 void setPriorityForTexture(const gfx::Rect& visibleRect,
526 const gfx::Rect& tileRect,
527 bool drawsToRoot,
528 bool isSmallAnimatedLayer,
529 PrioritizedResource* texture)
531 int priority = PriorityCalculator::lowestPriority();
532 if (!visibleRect.IsEmpty())
533 priority = PriorityCalculator::priorityFromDistance(visibleRect, tileRect, drawsToRoot);
534 if (isSmallAnimatedLayer)
535 priority = PriorityCalculator::maxPriority(priority, PriorityCalculator::smallAnimatedLayerMinPriority());
536 if (priority != PriorityCalculator::lowestPriority())
537 texture->setRequestPriority(priority);
539 } // namespace
541 void TiledLayer::setTexturePriorities(const PriorityCalculator& priorityCalc)
543 updateBounds();
544 resetUpdateState();
545 updateScrollPrediction();
547 if (m_tiler->hasEmptyBounds())
548 return;
550 bool drawsToRoot = !renderTarget()->parent();
551 bool smallAnimatedLayer = isSmallAnimatedLayer();
553 // Minimally create the tiles in the desired pre-paint rect.
554 gfx::Rect createTilesRect = idlePaintRect();
555 if (smallAnimatedLayer)
556 createTilesRect = gfx::Rect(gfx::Point(), contentBounds());
557 if (!createTilesRect.IsEmpty()) {
558 int left, top, right, bottom;
559 m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bottom);
560 for (int j = top; j <= bottom; ++j) {
561 for (int i = left; i <= right; ++i) {
562 if (!tileAt(i, j))
563 createTile(i, j);
568 // Now update priorities on all tiles we have in the layer, no matter where they are.
569 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
570 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
571 // FIXME: This should not ever be null.
572 if (!tile)
573 continue;
574 gfx::Rect tileRect = m_tiler->tileRect(tile);
575 setPriorityForTexture(m_predictedVisibleRect, tileRect, drawsToRoot, smallAnimatedLayer, tile->managedResource());
579 Region TiledLayer::visibleContentOpaqueRegion() const
581 if (m_skipsDraw)
582 return Region();
583 if (contentsOpaque())
584 return visibleContentRect();
585 return m_tiler->opaqueRegionInContentRect(visibleContentRect());
588 void TiledLayer::resetUpdateState()
590 m_skipsDraw = false;
591 m_failedUpdate = false;
593 LayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end();
594 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != end; ++iter) {
595 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
596 // FIXME: This should not ever be null.
597 if (!tile)
598 continue;
599 tile->resetUpdateState();
603 namespace {
604 gfx::Rect expandRectByDelta(gfx::Rect rect, gfx::Vector2d delta) {
605 int width = rect.width() + abs(delta.x());
606 int height = rect.height() + abs(delta.y());
607 int x = rect.x() + ((delta.x() < 0) ? delta.x() : 0);
608 int y = rect.y() + ((delta.y() < 0) ? delta.y() : 0);
609 return gfx::Rect(x, y, width, height);
613 void TiledLayer::updateScrollPrediction()
615 // This scroll prediction is very primitive and should be replaced by a
616 // a recursive calculation on all layers which uses actual scroll/animation
617 // velocities. To insure this doesn't miss-predict, we only use it to predict
618 // the visibleRect if:
619 // - contentBounds() hasn't changed.
620 // - visibleRect.size() hasn't changed.
621 // These two conditions prevent rotations, scales, pinch-zooms etc. where
622 // the prediction would be incorrect.
623 gfx::Vector2d delta = visibleContentRect().CenterPoint() - m_previousVisibleRect.CenterPoint();
624 m_predictedScroll = -delta;
625 m_predictedVisibleRect = visibleContentRect();
626 if (m_previousContentBounds == contentBounds() && m_previousVisibleRect.size() == visibleContentRect().size()) {
627 // Only expand the visible rect in the major scroll direction, to prevent
628 // massive paints due to diagonal scrolls.
629 gfx::Vector2d majorScrollDelta = (abs(delta.x()) > abs(delta.y())) ? gfx::Vector2d(delta.x(), 0) : gfx::Vector2d(0, delta.y());
630 m_predictedVisibleRect = expandRectByDelta(visibleContentRect(), majorScrollDelta);
632 // Bound the prediction to prevent unbounded paints, and clamp to content bounds.
633 gfx::Rect bound = visibleContentRect();
634 bound.Inset(-m_tiler->tileSize().width() * maxPredictiveTilesCount,
635 -m_tiler->tileSize().height() * maxPredictiveTilesCount);
636 bound.Intersect(gfx::Rect(gfx::Point(), contentBounds()));
637 m_predictedVisibleRect.Intersect(bound);
639 m_previousContentBounds = contentBounds();
640 m_previousVisibleRect = visibleContentRect();
643 void TiledLayer::update(ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats)
645 DCHECK(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped?
648 base::AutoReset<bool> ignoreSetNeedsCommit(&m_ignoreSetNeedsCommit, true);
650 ContentsScalingLayer::update(queue, occlusion, stats);
651 updateBounds();
654 if (m_tiler->hasEmptyBounds() || !drawsContent())
655 return;
657 bool didPaint = false;
659 // Animation pre-paint. If the layer is small, try to paint it all
660 // immediately whether or not it is occluded, to avoid paint/upload
661 // hiccups while it is animating.
662 if (isSmallAnimatedLayer()) {
663 int left, top, right, bottom;
664 m_tiler->contentRectToTileIndices(gfx::Rect(gfx::Point(), contentBounds()), left, top, right, bottom);
665 updateTiles(left, top, right, bottom, queue, 0, stats, didPaint);
666 if (didPaint)
667 return;
668 // This was an attempt to paint the entire layer so if we fail it's okay,
669 // just fallback on painting visible etc. below.
670 m_failedUpdate = false;
673 if (m_predictedVisibleRect.IsEmpty())
674 return;
676 // Visible painting. First occlude visible tiles and paint the non-occluded tiles.
677 int left, top, right, bottom;
678 m_tiler->contentRectToTileIndices(m_predictedVisibleRect, left, top, right, bottom);
679 markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
680 m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint);
681 if (m_skipsDraw)
682 m_tiler->reset();
683 if (m_skipsDraw || didPaint)
684 return;
686 // If we have already painting everything visible. Do some pre-painting while idle.
687 gfx::Rect idlePaintContentRect = idlePaintRect();
688 if (idlePaintContentRect.IsEmpty())
689 return;
691 // Prepaint anything that was occluded but inside the layer's visible region.
692 if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
693 return;
695 int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
696 m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
698 // Then expand outwards one row/column at a time until we find a dirty row/column
699 // to update. Increment along the major and minor scroll directions first.
700 gfx::Vector2d delta = -m_predictedScroll;
701 delta = gfx::Vector2d(delta.x() == 0 ? 1 : delta.x(),
702 delta.y() == 0 ? 1 : delta.y());
703 gfx::Vector2d majorDelta = (abs(delta.x()) > abs(delta.y())) ? gfx::Vector2d(delta.x(), 0) : gfx::Vector2d(0, delta.y());
704 gfx::Vector2d minorDelta = (abs(delta.x()) <= abs(delta.y())) ? gfx::Vector2d(delta.x(), 0) : gfx::Vector2d(0, delta.y());
705 gfx::Vector2d deltas[4] = {majorDelta, minorDelta, -majorDelta, -minorDelta};
706 for(int i = 0; i < 4; i++) {
707 if (deltas[i].y() > 0) {
708 while (bottom < prepaintBottom) {
709 ++bottom;
710 if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint)
711 return;
714 if (deltas[i].y() < 0) {
715 while (top > prepaintTop) {
716 --top;
717 if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint)
718 return;
721 if (deltas[i].x() < 0) {
722 while (left > prepaintLeft) {
723 --left;
724 if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint)
725 return;
728 if (deltas[i].x() > 0) {
729 while (right < prepaintRight) {
730 ++right;
731 if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
732 return;
738 bool TiledLayer::needsIdlePaint()
740 // Don't trigger more paints if we failed (as we'll just fail again).
741 if (m_failedUpdate || visibleContentRect().IsEmpty() || m_tiler->hasEmptyBounds() || !drawsContent())
742 return false;
744 gfx::Rect idlePaintContentRect = idlePaintRect();
745 if (idlePaintContentRect.IsEmpty())
746 return false;
748 int left, top, right, bottom;
749 m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom);
751 for (int j = top; j <= bottom; ++j) {
752 for (int i = left; i <= right; ++i) {
753 UpdatableTile* tile = tileAt(i, j);
754 DCHECK(tile); // Did setTexturePriorities get skipped?
755 if (!tile)
756 continue;
758 bool updated = !tile->updateRect.IsEmpty();
759 bool canAcquire = tile->managedResource()->canAcquireBackingTexture();
760 bool dirty = tile->isDirty() || !tile->managedResource()->haveBackingTexture();
761 if (!updated && canAcquire && dirty)
762 return true;
765 return false;
768 gfx::Rect TiledLayer::idlePaintRect()
770 // Don't inflate an empty rect.
771 if (visibleContentRect().IsEmpty())
772 return gfx::Rect();
774 gfx::Rect prepaintRect = visibleContentRect();
775 prepaintRect.Inset(-m_tiler->tileSize().width() * prepaintColumns,
776 -m_tiler->tileSize().height() * prepaintRows);
777 gfx::Rect contentRect(gfx::Point(), contentBounds());
778 prepaintRect.Intersect(contentRect);
780 return prepaintRect;
783 } // namespace cc