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"
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
{
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();
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.
62 updateRect
= dirtyRect
;
63 dirtyRect
= gfx::Rect();
73 explicit UpdatableTile(scoped_ptr
<LayerUpdater::Resource
> updaterResource
)
74 : partialUpdate(false)
75 , validForFrame(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
)
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
;
127 if (m_tilingOption
== AlwaysTile
)
129 else if (m_tilingOption
== NeverTile
)
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
)
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())
171 bool hasMoreThanOneTile
= m_tiler
->numTilesX() > 1 || m_tiler
->numTilesY() > 1;
172 if (m_tilingOption
== NeverTile
&& hasMoreThanOneTile
)
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.
206 if (!tile
->managedResource()->haveBackingTexture()) {
207 // Evicted tiles get deleted from both layers
208 invalidTiles
.push_back(tile
);
212 if (!tile
->validForFrame
) {
213 // Invalidated tiles are set so they can get different debug colors.
214 tiledLayer
->pushInvalidTile(i
, j
);
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
229 PrioritizedResourceManager
* TiledLayer::resourceManager() const
231 if (!layerTreeHost())
233 return layerTreeHost()->contentsTextureManager();
236 const PrioritizedResource
* TiledLayer::resourceAtForTesting(int i
, int j
) const
238 UpdatableTile
* tile
= tileAt(i
, j
);
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.
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.
282 void TiledLayer::setNeedsDisplayRect(const gfx::RectF
& dirtyRect
)
284 invalidateContentRect(layerRectToContentRect(dirtyRect
));
285 ContentsScalingLayer::setNeedsDisplayRect(dirtyRect
);
288 void TiledLayer::invalidateContentRect(const gfx::Rect
& contentRect
)
291 if (m_tiler
->isEmpty() || contentRect
.IsEmpty() || m_skipsDraw
)
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
);
297 // FIXME: This should not ever be null.
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
)
315 createUpdaterIfNeeded();
317 bool ignoreOcclusions
= !occlusion
;
318 if (!haveTexturesForTiles(left
, top
, right
, bottom
, ignoreOcclusions
)) {
319 m_failedUpdate
= true;
323 gfx::Rect paintRect
= markTilesForUpdate(left
, top
, right
, bottom
, ignoreOcclusions
);
326 occlusion
->overdrawMetrics().didPaint(paintRect
);
328 if (paintRect
.IsEmpty())
332 updateTileTextures(paintRect
, left
, top
, right
, bottom
, queue
, occlusion
, stats
);
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.
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(), isClipped(), clipRect())) {
356 tile
->occluded
= true;
359 succeeded
&= tile
->managedResource()->requestLate();
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.
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
)
389 if (!tile
->managedResource()->canAcquireBackingTexture())
396 gfx::Rect
TiledLayer::markTilesForUpdate(int left
, int top
, int right
, int bottom
, bool ignoreOcclusions
)
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.
406 if (tile
->occluded
&& !ignoreOcclusions
)
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;
418 tile
->dirtyRect
= m_tiler
->tileRect(tile
);
419 tile
->managedResource()->returnBackingTexture();
423 paintRect
.Union(tile
->dirtyRect
);
424 tile
->markForUpdate();
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.
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())
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())
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
);
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
511 bool TiledLayer::isSmallAnimatedLayer() const
513 if (!drawTransformIsAnimating() && !screenSpaceTransformIsAnimating())
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();
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
,
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
);
541 void TiledLayer::setTexturePriorities(const PriorityCalculator
& priorityCalc
)
545 updateScrollPrediction();
547 if (m_tiler
->hasEmptyBounds())
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
) {
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.
574 gfx::Rect tileRect
= m_tiler
->tileRect(tile
);
575 setPriorityForTexture(m_predictedVisibleRect
, tileRect
, drawsToRoot
, smallAnimatedLayer
, tile
->managedResource());
579 Region
TiledLayer::visibleContentOpaqueRegion() const
583 if (contentsOpaque())
584 return visibleContentRect();
585 return m_tiler
->opaqueRegionInContentRect(visibleContentRect());
588 void TiledLayer::resetUpdateState()
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.
599 tile
->resetUpdateState();
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
);
654 if (m_tiler
->hasEmptyBounds() || !drawsContent())
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
);
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())
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
);
683 if (m_skipsDraw
|| didPaint
)
686 // If we have already painting everything visible. Do some pre-painting while idle.
687 gfx::Rect idlePaintContentRect
= idlePaintRect();
688 if (idlePaintContentRect
.IsEmpty())
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
)
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
) {
710 if (!updateTiles(left
, bottom
, right
, bottom
, queue
, 0, stats
, didPaint
) || didPaint
)
714 if (deltas
[i
].y() < 0) {
715 while (top
> prepaintTop
) {
717 if (!updateTiles(left
, top
, right
, top
, queue
, 0, stats
, didPaint
) || didPaint
)
721 if (deltas
[i
].x() < 0) {
722 while (left
> prepaintLeft
) {
724 if (!updateTiles(left
, top
, left
, bottom
, queue
, 0, stats
, didPaint
) || didPaint
)
728 if (deltas
[i
].x() > 0) {
729 while (right
< prepaintRight
) {
731 if (!updateTiles(right
, top
, right
, bottom
, queue
, 0, stats
, didPaint
) || didPaint
)
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())
744 gfx::Rect idlePaintContentRect
= idlePaintRect();
745 if (idlePaintContentRect
.IsEmpty())
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?
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
)
768 gfx::Rect
TiledLayer::idlePaintRect()
770 // Don't inflate an empty rect.
771 if (visibleContentRect().IsEmpty())
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
);