blob: 4d4a355a62ab310aba79d3589ce82684736f49fb [file] [log] [blame]
[email protected]94f206c12012-08-25 00:09:141// 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.
4
5#include "config.h"
6
[email protected]a8461d82012-10-16 21:11:147#include "cc/tiled_layer.h"
[email protected]94f206c12012-08-25 00:09:148
[email protected]94f206c12012-08-25 00:09:149#include "Region.h"
[email protected]a8461d82012-10-16 21:11:1410#include "base/basictypes.h"
[email protected]aad0a0072012-11-01 18:15:5811#include "cc/geometry.h"
[email protected]d50c6862012-10-23 02:08:3112#include "cc/layer_impl.h"
13#include "cc/layer_tree_host.h"
[email protected]55a124d02012-10-22 03:07:1314#include "cc/overdraw_metrics.h"
[email protected]da2c9122012-10-20 23:13:0615#include "cc/tiled_layer_impl.h"
[email protected]d9c28522012-10-18 23:35:4316#include "third_party/khronos/GLES2/gl2.h"
[email protected]aad0a0072012-11-01 18:15:5817#include "ui/gfx/rect_conversions.h"
[email protected]94f206c12012-08-25 00:09:1418
19using namespace std;
20using WebKit::WebTransformationMatrix;
21
[email protected]9c88e562012-09-14 22:21:3022namespace cc {
[email protected]94f206c12012-08-25 00:09:1423
[email protected]8d931722012-10-23 20:26:4624// Maximum predictive expansion of the visible area.
25static const int maxPredictiveTilesCount = 2;
26
27// Number of rows/columns of tiles to pre-paint.
28// We should increase these further as all textures are
29// prioritized and we insure performance doesn't suffer.
30static const int prepaintRows = 4;
31static const int prepaintColumns = 2;
32
33
[email protected]96baf3e2012-10-22 23:09:5534class UpdatableTile : public LayerTilingData::Tile {
[email protected]94f206c12012-08-25 00:09:1435public:
[email protected]f33db562012-10-25 03:25:5536 static scoped_ptr<UpdatableTile> create(scoped_ptr<LayerUpdater::Resource> updaterResource)
[email protected]94f206c12012-08-25 00:09:1437 {
[email protected]f33db562012-10-25 03:25:5538 return make_scoped_ptr(new UpdatableTile(updaterResource.Pass()));
[email protected]94f206c12012-08-25 00:09:1439 }
40
[email protected]f33db562012-10-25 03:25:5541 LayerUpdater::Resource* updaterResource() { return m_updaterResource.get(); }
42 PrioritizedTexture* managedTexture() { return m_updaterResource->texture(); }
[email protected]94f206c12012-08-25 00:09:1443
[email protected]aad0a0072012-11-01 18:15:5844 bool isDirty() const { return !dirtyRect.IsEmpty(); }
[email protected]94f206c12012-08-25 00:09:1445
46 // Reset update state for the current frame. This should occur before painting
47 // for all layers. Since painting one layer can invalidate another layer
48 // after it has already painted, mark all non-dirty tiles as valid before painting
49 // such that invalidations during painting won't prevent them from being pushed.
50 void resetUpdateState()
51 {
[email protected]aad0a0072012-11-01 18:15:5852 updateRect = gfx::Rect();
[email protected]94f206c12012-08-25 00:09:1453 occluded = false;
54 partialUpdate = false;
55 validForFrame = !isDirty();
56 }
57
58 // This promises to update the tile and therefore also guarantees the tile
59 // will be valid for this frame. dirtyRect is copied into updateRect so
60 // we can continue to track re-entrant invalidations that occur during painting.
61 void markForUpdate()
62 {
63 validForFrame = true;
64 updateRect = dirtyRect;
[email protected]aad0a0072012-11-01 18:15:5865 dirtyRect = gfx::Rect();
[email protected]94f206c12012-08-25 00:09:1466 }
67
[email protected]aad0a0072012-11-01 18:15:5868 gfx::Rect dirtyRect;
69 gfx::Rect updateRect;
[email protected]94f206c12012-08-25 00:09:1470 bool partialUpdate;
71 bool validForFrame;
72 bool occluded;
[email protected]321fe3702012-10-16 17:32:4873
[email protected]94f206c12012-08-25 00:09:1474private:
[email protected]f33db562012-10-25 03:25:5575 explicit UpdatableTile(scoped_ptr<LayerUpdater::Resource> updaterResource)
[email protected]94f206c12012-08-25 00:09:1476 : partialUpdate(false)
77 , validForFrame(false)
78 , occluded(false)
[email protected]f33db562012-10-25 03:25:5579 , m_updaterResource(updaterResource.Pass())
[email protected]94f206c12012-08-25 00:09:1480 {
81 }
82
[email protected]f33db562012-10-25 03:25:5583 scoped_ptr<LayerUpdater::Resource> m_updaterResource;
[email protected]fd2d4f22012-09-28 22:57:2084
85 DISALLOW_COPY_AND_ASSIGN(UpdatableTile);
[email protected]94f206c12012-08-25 00:09:1486};
87
[email protected]96baf3e2012-10-22 23:09:5588TiledLayer::TiledLayer()
[email protected]904e9132012-11-01 00:12:4789 : ContentsScalingLayer()
[email protected]d9c28522012-10-18 23:35:4390 , m_textureFormat(GL_INVALID_ENUM)
[email protected]94f206c12012-08-25 00:09:1491 , m_skipsDraw(false)
92 , m_failedUpdate(false)
[email protected]94f206c12012-08-25 00:09:1493 , m_tilingOption(AutoTile)
94{
[email protected]aad0a0072012-11-01 18:15:5895 m_tiler = LayerTilingData::create(gfx::Size(), LayerTilingData::HasBorderTexels);
[email protected]94f206c12012-08-25 00:09:1496}
97
[email protected]96baf3e2012-10-22 23:09:5598TiledLayer::~TiledLayer()
[email protected]94f206c12012-08-25 00:09:1499{
100}
101
[email protected]96baf3e2012-10-22 23:09:55102scoped_ptr<LayerImpl> TiledLayer::createLayerImpl()
[email protected]94f206c12012-08-25 00:09:14103{
[email protected]96baf3e2012-10-22 23:09:55104 return TiledLayerImpl::create(id()).PassAs<LayerImpl>();
[email protected]94f206c12012-08-25 00:09:14105}
106
[email protected]96baf3e2012-10-22 23:09:55107void TiledLayer::updateTileSizeAndTilingOption()
[email protected]94f206c12012-08-25 00:09:14108{
[email protected]1d993172012-10-18 18:15:04109 DCHECK(layerTreeHost());
[email protected]94f206c12012-08-25 00:09:14110
[email protected]aad0a0072012-11-01 18:15:58111 gfx::Size defaultTileSize = layerTreeHost()->settings().defaultTileSize;
112 gfx::Size maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledLayerSize;
[email protected]94f206c12012-08-25 00:09:14113 int layerWidth = contentBounds().width();
114 int layerHeight = contentBounds().height();
115
[email protected]aad0a0072012-11-01 18:15:58116 gfx::Size tileSize(min(defaultTileSize.width(), layerWidth), min(defaultTileSize.height(), layerHeight));
[email protected]94f206c12012-08-25 00:09:14117
118 // Tile if both dimensions large, or any one dimension large and the other
119 // extends into a second tile but the total layer area isn't larger than that
120 // of the largest possible untiled layer. This heuristic allows for long skinny layers
121 // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but still avoids
122 // creating very large tiles.
[email protected]aad0a0072012-11-01 18:15:58123 bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || layerHeight > maxUntiledLayerSize.height();
124 bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || layerHeight <= defaultTileSize.height())
[email protected]94f206c12012-08-25 00:09:14125 && (layerWidth * layerHeight) <= (maxUntiledLayerSize.width() * maxUntiledLayerSize.height());
[email protected]aad0a0072012-11-01 18:15:58126 bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
[email protected]94f206c12012-08-25 00:09:14127
128 bool isTiled;
129 if (m_tilingOption == AlwaysTile)
130 isTiled = true;
131 else if (m_tilingOption == NeverTile)
132 isTiled = false;
133 else
134 isTiled = autoTiled;
135
[email protected]aad0a0072012-11-01 18:15:58136 gfx::Size requestedSize = isTiled ? tileSize : contentBounds();
[email protected]94f206c12012-08-25 00:09:14137 const int maxSize = layerTreeHost()->rendererCapabilities().maxTextureSize;
[email protected]aad0a0072012-11-01 18:15:58138 gfx::Size clampedSize = ClampSizeFromAbove(requestedSize, gfx::Size(maxSize, maxSize));
[email protected]94f206c12012-08-25 00:09:14139 setTileSize(clampedSize);
140}
141
[email protected]96baf3e2012-10-22 23:09:55142void TiledLayer::updateBounds()
[email protected]94f206c12012-08-25 00:09:14143{
[email protected]aad0a0072012-11-01 18:15:58144 gfx::Size oldBounds = m_tiler->bounds();
145 gfx::Size newBounds = contentBounds();
[email protected]94f206c12012-08-25 00:09:14146 if (oldBounds == newBounds)
147 return;
148 m_tiler->setBounds(newBounds);
149
150 // Invalidate any areas that the new bounds exposes.
[email protected]d0f98362012-11-01 23:02:38151 Region oldRegion = gfx::Rect(gfx::Point(), oldBounds);
152 Region newRegion = gfx::Rect(gfx::Point(), newBounds);
153 newRegion.Subtract(oldRegion);
[email protected]0d8a30502012-11-03 02:59:15154 for (Region::Iterator newRects(newRegion); newRects.has_rect(); newRects.next())
155 invalidateContentRect(newRects.rect());
[email protected]94f206c12012-08-25 00:09:14156}
157
[email protected]aad0a0072012-11-01 18:15:58158void TiledLayer::setTileSize(const gfx::Size& size)
[email protected]94f206c12012-08-25 00:09:14159{
160 m_tiler->setTileSize(size);
161}
162
[email protected]96baf3e2012-10-22 23:09:55163void TiledLayer::setBorderTexelOption(LayerTilingData::BorderTexelOption borderTexelOption)
[email protected]94f206c12012-08-25 00:09:14164{
165 m_tiler->setBorderTexelOption(borderTexelOption);
166}
167
[email protected]96baf3e2012-10-22 23:09:55168bool TiledLayer::drawsContent() const
[email protected]94f206c12012-08-25 00:09:14169{
[email protected]904e9132012-11-01 00:12:47170 if (!ContentsScalingLayer::drawsContent())
[email protected]94f206c12012-08-25 00:09:14171 return false;
172
173 bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() > 1;
174 if (m_tilingOption == NeverTile && hasMoreThanOneTile)
175 return false;
176
177 return true;
178}
179
[email protected]96baf3e2012-10-22 23:09:55180void TiledLayer::setTilingOption(TilingOption tilingOption)
[email protected]94f206c12012-08-25 00:09:14181{
182 m_tilingOption = tilingOption;
183}
184
[email protected]96baf3e2012-10-22 23:09:55185void TiledLayer::setIsMask(bool isMask)
[email protected]94f206c12012-08-25 00:09:14186{
187 setTilingOption(isMask ? NeverTile : AutoTile);
188}
189
[email protected]96baf3e2012-10-22 23:09:55190void TiledLayer::pushPropertiesTo(LayerImpl* layer)
[email protected]94f206c12012-08-25 00:09:14191{
[email protected]904e9132012-11-01 00:12:47192 ContentsScalingLayer::pushPropertiesTo(layer);
[email protected]94f206c12012-08-25 00:09:14193
[email protected]96baf3e2012-10-22 23:09:55194 TiledLayerImpl* tiledLayer = static_cast<TiledLayerImpl*>(layer);
[email protected]94f206c12012-08-25 00:09:14195
196 tiledLayer->setSkipsDraw(m_skipsDraw);
[email protected]94f206c12012-08-25 00:09:14197 tiledLayer->setTilingData(*m_tiler);
[email protected]787465c2012-10-29 01:12:27198 std::vector<UpdatableTile*> invalidTiles;
[email protected]94f206c12012-08-25 00:09:14199
[email protected]96baf3e2012-10-22 23:09:55200 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
[email protected]94f206c12012-08-25 00:09:14201 int i = iter->first.first;
202 int j = iter->first.second;
[email protected]0def90d2012-10-15 23:16:49203 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
[email protected]94f206c12012-08-25 00:09:14204 // FIXME: This should not ever be null.
205 if (!tile)
206 continue;
[email protected]a416a0be2012-09-28 22:00:10207
[email protected]94f206c12012-08-25 00:09:14208 if (!tile->managedTexture()->haveBackingTexture()) {
[email protected]a416a0be2012-09-28 22:00:10209 // Evicted tiles get deleted from both layers
[email protected]787465c2012-10-29 01:12:27210 invalidTiles.push_back(tile);
[email protected]94f206c12012-08-25 00:09:14211 continue;
212 }
[email protected]a416a0be2012-09-28 22:00:10213
214 if (!tile->validForFrame) {
215 // Invalidated tiles are set so they can get different debug colors.
216 tiledLayer->pushInvalidTile(i, j);
[email protected]94f206c12012-08-25 00:09:14217 continue;
[email protected]a416a0be2012-09-28 22:00:10218 }
[email protected]94f206c12012-08-25 00:09:14219
[email protected]fdc76932012-10-22 19:51:31220 tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(), tile->opaqueRect(), tile->managedTexture()->contentsSwizzled());
[email protected]94f206c12012-08-25 00:09:14221 }
[email protected]787465c2012-10-29 01:12:27222 for (std::vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter)
[email protected]94f206c12012-08-25 00:09:14223 m_tiler->takeTile((*iter)->i(), (*iter)->j());
224}
225
[email protected]96baf3e2012-10-22 23:09:55226PrioritizedTextureManager* TiledLayer::textureManager() const
[email protected]94f206c12012-08-25 00:09:14227{
228 if (!layerTreeHost())
229 return 0;
230 return layerTreeHost()->contentsTextureManager();
231}
232
[email protected]96baf3e2012-10-22 23:09:55233void TiledLayer::setLayerTreeHost(LayerTreeHost* host)
[email protected]94f206c12012-08-25 00:09:14234{
235 if (host && host != layerTreeHost()) {
[email protected]96baf3e2012-10-22 23:09:55236 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
[email protected]0def90d2012-10-15 23:16:49237 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
[email protected]94f206c12012-08-25 00:09:14238 // FIXME: This should not ever be null.
239 if (!tile)
240 continue;
241 tile->managedTexture()->setTextureManager(host->contentsTextureManager());
242 }
243 }
[email protected]904e9132012-11-01 00:12:47244 ContentsScalingLayer::setLayerTreeHost(host);
[email protected]94f206c12012-08-25 00:09:14245}
246
[email protected]96baf3e2012-10-22 23:09:55247UpdatableTile* TiledLayer::tileAt(int i, int j) const
[email protected]94f206c12012-08-25 00:09:14248{
249 return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
250}
251
[email protected]96baf3e2012-10-22 23:09:55252UpdatableTile* TiledLayer::createTile(int i, int j)
[email protected]94f206c12012-08-25 00:09:14253{
[email protected]64d8d392012-10-23 18:34:51254 createUpdaterIfNeeded();
[email protected]94f206c12012-08-25 00:09:14255
[email protected]f33db562012-10-25 03:25:55256 scoped_ptr<UpdatableTile> tile(UpdatableTile::create(updater()->createResource(textureManager())));
[email protected]94f206c12012-08-25 00:09:14257 tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat);
258
259 UpdatableTile* addedTile = tile.get();
[email protected]96baf3e2012-10-22 23:09:55260 m_tiler->addTile(tile.PassAs<LayerTilingData::Tile>(), i, j);
[email protected]94f206c12012-08-25 00:09:14261
262 addedTile->dirtyRect = m_tiler->tileRect(addedTile);
263
264 // Temporary diagnostic crash.
265 if (!addedTile)
266 CRASH();
267 if (!tileAt(i, j))
268 CRASH();
269
270 return addedTile;
271}
272
[email protected]aad0a0072012-11-01 18:15:58273void TiledLayer::setNeedsDisplayRect(const gfx::RectF& dirtyRect)
[email protected]94f206c12012-08-25 00:09:14274{
[email protected]904e9132012-11-01 00:12:47275 invalidateContentRect(layerRectToContentRect(dirtyRect));
276 ContentsScalingLayer::setNeedsDisplayRect(dirtyRect);
[email protected]94f206c12012-08-25 00:09:14277}
278
[email protected]96baf3e2012-10-22 23:09:55279void TiledLayer::setUseLCDText(bool useLCDText)
[email protected]94f206c12012-08-25 00:09:14280{
[email protected]904e9132012-11-01 00:12:47281 ContentsScalingLayer::setUseLCDText(useLCDText);
[email protected]94f206c12012-08-25 00:09:14282
[email protected]96baf3e2012-10-22 23:09:55283 LayerTilingData::BorderTexelOption borderTexelOption;
[email protected]94f206c12012-08-25 00:09:14284#if OS(ANDROID)
285 // Always want border texels and GL_LINEAR due to pinch zoom.
[email protected]96baf3e2012-10-22 23:09:55286 borderTexelOption = LayerTilingData::HasBorderTexels;
[email protected]94f206c12012-08-25 00:09:14287#else
[email protected]96baf3e2012-10-22 23:09:55288 borderTexelOption = useLCDText ? LayerTilingData::NoBorderTexels : LayerTilingData::HasBorderTexels;
[email protected]94f206c12012-08-25 00:09:14289#endif
290 setBorderTexelOption(borderTexelOption);
291}
292
[email protected]aad0a0072012-11-01 18:15:58293void TiledLayer::invalidateContentRect(const gfx::Rect& contentRect)
[email protected]94f206c12012-08-25 00:09:14294{
295 updateBounds();
[email protected]aad0a0072012-11-01 18:15:58296 if (m_tiler->isEmpty() || contentRect.IsEmpty() || m_skipsDraw)
[email protected]94f206c12012-08-25 00:09:14297 return;
298
[email protected]96baf3e2012-10-22 23:09:55299 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
[email protected]0def90d2012-10-15 23:16:49300 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
[email protected]1d993172012-10-18 18:15:04301 DCHECK(tile);
[email protected]94f206c12012-08-25 00:09:14302 // FIXME: This should not ever be null.
303 if (!tile)
304 continue;
[email protected]aad0a0072012-11-01 18:15:58305 gfx::Rect bound = m_tiler->tileRect(tile);
306 bound.Intersect(contentRect);
307 tile->dirtyRect.Union(bound);
[email protected]94f206c12012-08-25 00:09:14308 }
309}
310
311// Returns true if tile is dirty and only part of it needs to be updated.
[email protected]96baf3e2012-10-22 23:09:55312bool TiledLayer::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
[email protected]94f206c12012-08-25 00:09:14313{
[email protected]aad0a0072012-11-01 18:15:58314 return !tile->dirtyRect.Contains(m_tiler->tileRect(tile)) && tile->managedTexture()->haveBackingTexture();
[email protected]94f206c12012-08-25 00:09:14315}
316
[email protected]b4da2032012-10-25 21:22:55317bool TiledLayer::updateTiles(int left, int top, int right, int bottom, ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats, bool& didPaint)
[email protected]94f206c12012-08-25 00:09:14318{
319 didPaint = false;
[email protected]64d8d392012-10-23 18:34:51320 createUpdaterIfNeeded();
[email protected]94f206c12012-08-25 00:09:14321
322 bool ignoreOcclusions = !occlusion;
323 if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) {
324 m_failedUpdate = true;
325 return false;
326 }
327
[email protected]aad0a0072012-11-01 18:15:58328 gfx::Rect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclusions);
[email protected]94f206c12012-08-25 00:09:14329
330 if (occlusion)
[email protected]d0f98362012-11-01 23:02:38331 occlusion->overdrawMetrics().didPaint(paintRect);
[email protected]94f206c12012-08-25 00:09:14332
[email protected]aad0a0072012-11-01 18:15:58333 if (paintRect.IsEmpty())
[email protected]94f206c12012-08-25 00:09:14334 return true;
335
336 didPaint = true;
337 updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, stats);
338 return true;
339}
340
[email protected]96baf3e2012-10-22 23:09:55341void TiledLayer::markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const OcclusionTracker* occlusion)
[email protected]94f206c12012-08-25 00:09:14342{
343 // There is some difficult dependancies between occlusions, recording occlusion metrics
344 // and requesting memory so those are encapsulated in this function:
345 // - We only want to call requestLate on unoccluded textures (to preserve
346 // memory for other layers when near OOM).
347 // - We only want to record occlusion metrics if all memory requests succeed.
348
349 int occludedTileCount = 0;
350 bool succeeded = true;
351 for (int j = top; j <= bottom; ++j) {
352 for (int i = left; i <= right; ++i) {
353 UpdatableTile* tile = tileAt(i, j);
[email protected]1d993172012-10-18 18:15:04354 DCHECK(tile); // Did setTexturePriorities get skipped?
[email protected]94f206c12012-08-25 00:09:14355 // FIXME: This should not ever be null.
356 if (!tile)
357 continue;
[email protected]1d993172012-10-18 18:15:04358 DCHECK(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occlusion pass?
[email protected]aad0a0072012-11-01 18:15:58359 gfx::Rect visibleTileRect = gfx::IntersectRects(m_tiler->tileBounds(i, j), visibleContentRect());
[email protected]710ffc02012-10-30 21:42:02360 if (occlusion && occlusion->occluded(renderTarget(), visibleTileRect, drawTransform(), drawTransformIsAnimating(), drawableContentRect())) {
[email protected]94f206c12012-08-25 00:09:14361 tile->occluded = true;
362 occludedTileCount++;
363 } else {
364 succeeded &= tile->managedTexture()->requestLate();
365 }
366 }
367 }
368
369 if (!succeeded)
370 return;
[email protected]4b685d32012-10-25 19:06:40371 if (occlusion)
372 occlusion->overdrawMetrics().didCullTilesForUpload(occludedTileCount);
[email protected]94f206c12012-08-25 00:09:14373}
374
[email protected]96baf3e2012-10-22 23:09:55375bool TiledLayer::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions)
[email protected]94f206c12012-08-25 00:09:14376{
377 for (int j = top; j <= bottom; ++j) {
378 for (int i = left; i <= right; ++i) {
379 UpdatableTile* tile = tileAt(i, j);
[email protected]1d993172012-10-18 18:15:04380 DCHECK(tile); // Did setTexturePriorites get skipped?
[email protected]94f206c12012-08-25 00:09:14381 // FIXME: This should not ever be null.
382 if (!tile)
383 continue;
384
385 // Ensure the entire tile is dirty if we don't have the texture.
386 if (!tile->managedTexture()->haveBackingTexture())
387 tile->dirtyRect = m_tiler->tileRect(tile);
388
389 // If using occlusion and the visible region of the tile is occluded,
390 // don't reserve a texture or update the tile.
391 if (tile->occluded && !ignoreOcclusions)
392 continue;
393
394 if (!tile->managedTexture()->canAcquireBackingTexture())
395 return false;
396 }
397 }
398 return true;
399}
400
[email protected]aad0a0072012-11-01 18:15:58401gfx::Rect TiledLayer::markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions)
[email protected]94f206c12012-08-25 00:09:14402{
[email protected]aad0a0072012-11-01 18:15:58403 gfx::Rect paintRect;
[email protected]94f206c12012-08-25 00:09:14404 for (int j = top; j <= bottom; ++j) {
405 for (int i = left; i <= right; ++i) {
406 UpdatableTile* tile = tileAt(i, j);
[email protected]1d993172012-10-18 18:15:04407 DCHECK(tile); // Did setTexturePriorites get skipped?
[email protected]94f206c12012-08-25 00:09:14408 // FIXME: This should not ever be null.
409 if (!tile)
410 continue;
411 if (tile->occluded && !ignoreOcclusions)
412 continue;
[email protected]4b685d32012-10-25 19:06:40413 // FIXME: Decide if partial update should be allowed based on cost
414 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
415 if (tile->isDirty() && layerTreeHost() && layerTreeHost()->bufferedUpdates()) {
416 // If we get a partial update, we use the same texture, otherwise return the
417 // current texture backing, so we don't update visible textures non-atomically.
418 // If the current backing is in-use, it won't be deleted until after the commit
419 // as the texture manager will not allow deletion or recycling of in-use textures.
420 if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate())
421 tile->partialUpdate = true;
422 else {
423 tile->dirtyRect = m_tiler->tileRect(tile);
424 tile->managedTexture()->returnBackingTexture();
425 }
426 }
427
[email protected]aad0a0072012-11-01 18:15:58428 paintRect.Union(tile->dirtyRect);
[email protected]94f206c12012-08-25 00:09:14429 tile->markForUpdate();
430 }
431 }
432 return paintRect;
433}
434
[email protected]aad0a0072012-11-01 18:15:58435void TiledLayer::updateTileTextures(const gfx::Rect& paintRect, int left, int top, int right, int bottom, ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats)
[email protected]94f206c12012-08-25 00:09:14436{
437 // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space.
[email protected]94f206c12012-08-25 00:09:14438 float widthScale = bounds().width() / static_cast<float>(contentBounds().width());
439 float heightScale = bounds().height() / static_cast<float>(contentBounds().height());
[email protected]aad0a0072012-11-01 18:15:58440 m_updateRect = gfx::ScaleRect(paintRect, widthScale, heightScale);
[email protected]94f206c12012-08-25 00:09:14441
442 // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
443 // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
444 // However, we can't free the memory backing the SkCanvas until the paint finishes,
445 // so we grab a local reference here to hold the updater alive until the paint completes.
[email protected]64d8d392012-10-23 18:34:51446 scoped_refptr<LayerUpdater> protector(updater());
[email protected]f809d3bb2012-10-31 20:52:25447 gfx::Rect paintedOpaqueRect;
[email protected]64d8d392012-10-23 18:34:51448 updater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthScale, 1 / heightScale, paintedOpaqueRect, stats);
[email protected]94f206c12012-08-25 00:09:14449
450 for (int j = top; j <= bottom; ++j) {
451 for (int i = left; i <= right; ++i) {
452 UpdatableTile* tile = tileAt(i, j);
[email protected]1d993172012-10-18 18:15:04453 DCHECK(tile); // Did setTexturePriorites get skipped?
[email protected]94f206c12012-08-25 00:09:14454 // FIXME: This should not ever be null.
455 if (!tile)
456 continue;
457
[email protected]aad0a0072012-11-01 18:15:58458 gfx::Rect tileRect = m_tiler->tileBounds(i, j);
[email protected]94f206c12012-08-25 00:09:14459
460 // Use updateRect as the above loop copied the dirty rect for this frame to updateRect.
[email protected]aad0a0072012-11-01 18:15:58461 const gfx::Rect& dirtyRect = tile->updateRect;
462 if (dirtyRect.IsEmpty())
[email protected]94f206c12012-08-25 00:09:14463 continue;
464
465 // Save what was painted opaque in the tile. Keep the old area if the paint didn't touch it, and didn't paint some
466 // other part of the tile opaque.
[email protected]aad0a0072012-11-01 18:15:58467 gfx::Rect tilePaintedRect = gfx::IntersectRects(tileRect, paintRect);
468 gfx::Rect tilePaintedOpaqueRect = gfx::IntersectRects(tileRect, paintedOpaqueRect);
469 if (!tilePaintedRect.IsEmpty()) {
470 gfx::Rect paintInsideTileOpaqueRect = gfx::IntersectRects(tile->opaqueRect(), tilePaintedRect);
471 bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRect.Contains(paintInsideTileOpaqueRect);
472 bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect.IsEmpty() && !tile->opaqueRect().Contains(tilePaintedOpaqueRect);
[email protected]94f206c12012-08-25 00:09:14473
474 if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInsideTileOpaqueRect)
475 tile->setOpaqueRect(tilePaintedOpaqueRect);
476 }
477
478 // sourceRect starts as a full-sized tile with border texels included.
[email protected]aad0a0072012-11-01 18:15:58479 gfx::Rect sourceRect = m_tiler->tileRect(tile);
480 sourceRect.Intersect(dirtyRect);
[email protected]94f206c12012-08-25 00:09:14481 // Paint rect not guaranteed to line up on tile boundaries, so
482 // make sure that sourceRect doesn't extend outside of it.
[email protected]aad0a0072012-11-01 18:15:58483 sourceRect.Intersect(paintRect);
[email protected]94f206c12012-08-25 00:09:14484
485 tile->updateRect = sourceRect;
486
[email protected]aad0a0072012-11-01 18:15:58487 if (sourceRect.IsEmpty())
[email protected]94f206c12012-08-25 00:09:14488 continue;
489
[email protected]aad0a0072012-11-01 18:15:58490 const gfx::Point anchor = m_tiler->tileRect(tile).origin();
[email protected]94f206c12012-08-25 00:09:14491
492 // Calculate tile-space rectangle to upload into.
[email protected]aad0a0072012-11-01 18:15:58493 gfx::Vector2d destOffset = sourceRect.origin() - anchor;
[email protected]f809d3bb2012-10-31 20:52:25494 if (destOffset.x() < 0)
[email protected]94f206c12012-08-25 00:09:14495 CRASH();
[email protected]f809d3bb2012-10-31 20:52:25496 if (destOffset.y() < 0)
[email protected]94f206c12012-08-25 00:09:14497 CRASH();
498
499 // Offset from paint rectangle to this tile's dirty rectangle.
[email protected]aad0a0072012-11-01 18:15:58500 gfx::Vector2d paintOffset = sourceRect.origin() - paintRect.origin();
[email protected]94f206c12012-08-25 00:09:14501 if (paintOffset.x() < 0)
502 CRASH();
503 if (paintOffset.y() < 0)
504 CRASH();
505 if (paintOffset.x() + sourceRect.width() > paintRect.width())
506 CRASH();
507 if (paintOffset.y() + sourceRect.height() > paintRect.height())
508 CRASH();
509
[email protected]f33db562012-10-25 03:25:55510 tile->updaterResource()->update(queue, sourceRect, destOffset, tile->partialUpdate, stats);
[email protected]ea8af90e2012-10-13 10:20:27511 if (occlusion)
[email protected]d0f98362012-11-01 23:02:38512 occlusion->overdrawMetrics().didUpload(WebTransformationMatrix(), sourceRect, tile->opaqueRect());
[email protected]ea8af90e2012-10-13 10:20:27513
[email protected]94f206c12012-08-25 00:09:14514 }
515 }
516}
517
518namespace {
519// This picks a small animated layer to be anything less than one viewport. This
520// is specifically for page transitions which are viewport-sized layers. The extra
521// 64 pixels is due to these layers being slightly larger than the viewport in some cases.
[email protected]96baf3e2012-10-22 23:09:55522bool isSmallAnimatedLayer(TiledLayer* layer)
[email protected]94f206c12012-08-25 00:09:14523{
524 if (!layer->drawTransformIsAnimating() && !layer->screenSpaceTransformIsAnimating())
525 return false;
[email protected]aad0a0072012-11-01 18:15:58526 gfx::Size viewportSize = layer->layerTreeHost() ? layer->layerTreeHost()->deviceViewportSize() : gfx::Size();
527 gfx::Rect contentRect(gfx::Point(), layer->contentBounds());
[email protected]94f206c12012-08-25 00:09:14528 return contentRect.width() <= viewportSize.width() + 64
529 && contentRect.height() <= viewportSize.height() + 64;
530}
531
532// FIXME: Remove this and make this based on distance once distance can be calculated
533// for offscreen layers. For now, prioritize all small animated layers after 512
534// pixels of pre-painting.
[email protected]aad0a0072012-11-01 18:15:58535void setPriorityForTexture(const gfx::Rect& visibleRect,
536 const gfx::Rect& tileRect,
[email protected]94f206c12012-08-25 00:09:14537 bool drawsToRoot,
538 bool isSmallAnimatedLayer,
[email protected]96baf3e2012-10-22 23:09:55539 PrioritizedTexture* texture)
[email protected]94f206c12012-08-25 00:09:14540{
[email protected]96baf3e2012-10-22 23:09:55541 int priority = PriorityCalculator::lowestPriority();
[email protected]aad0a0072012-11-01 18:15:58542 if (!visibleRect.IsEmpty())
[email protected]96baf3e2012-10-22 23:09:55543 priority = PriorityCalculator::priorityFromDistance(visibleRect, tileRect, drawsToRoot);
[email protected]94f206c12012-08-25 00:09:14544 if (isSmallAnimatedLayer)
[email protected]96baf3e2012-10-22 23:09:55545 priority = PriorityCalculator::maxPriority(priority, PriorityCalculator::smallAnimatedLayerMinPriority());
546 if (priority != PriorityCalculator::lowestPriority())
[email protected]94f206c12012-08-25 00:09:14547 texture->setRequestPriority(priority);
548}
549}
550
[email protected]96baf3e2012-10-22 23:09:55551void TiledLayer::setTexturePriorities(const PriorityCalculator& priorityCalc)
[email protected]94f206c12012-08-25 00:09:14552{
553 updateBounds();
554 resetUpdateState();
[email protected]8d931722012-10-23 20:26:46555 updateScrollPrediction();
[email protected]94f206c12012-08-25 00:09:14556
557 if (m_tiler->hasEmptyBounds())
558 return;
559
560 bool drawsToRoot = !renderTarget()->parent();
561 bool smallAnimatedLayer = isSmallAnimatedLayer(this);
562
563 // Minimally create the tiles in the desired pre-paint rect.
[email protected]aad0a0072012-11-01 18:15:58564 gfx::Rect createTilesRect = idlePaintRect();
[email protected]4b685d32012-10-25 19:06:40565 if (smallAnimatedLayer)
[email protected]aad0a0072012-11-01 18:15:58566 createTilesRect = gfx::Rect(gfx::Point(), contentBounds());
567 if (!createTilesRect.IsEmpty()) {
[email protected]94f206c12012-08-25 00:09:14568 int left, top, right, bottom;
569 m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bottom);
570 for (int j = top; j <= bottom; ++j) {
571 for (int i = left; i <= right; ++i) {
572 if (!tileAt(i, j))
573 createTile(i, j);
574 }
575 }
576 }
577
[email protected]94f206c12012-08-25 00:09:14578 // Now update priorities on all tiles we have in the layer, no matter where they are.
[email protected]96baf3e2012-10-22 23:09:55579 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
[email protected]0def90d2012-10-15 23:16:49580 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
[email protected]94f206c12012-08-25 00:09:14581 // FIXME: This should not ever be null.
582 if (!tile)
583 continue;
[email protected]aad0a0072012-11-01 18:15:58584 gfx::Rect tileRect = m_tiler->tileRect(tile);
[email protected]8d931722012-10-23 20:26:46585 setPriorityForTexture(m_predictedVisibleRect, tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture());
[email protected]94f206c12012-08-25 00:09:14586 }
587}
588
[email protected]96baf3e2012-10-22 23:09:55589Region TiledLayer::visibleContentOpaqueRegion() const
[email protected]94f206c12012-08-25 00:09:14590{
591 if (m_skipsDraw)
592 return Region();
[email protected]048634c2012-10-02 22:33:14593 if (contentsOpaque())
[email protected]d0f98362012-11-01 23:02:38594 return visibleContentRect();
[email protected]94f206c12012-08-25 00:09:14595 return m_tiler->opaqueRegionInContentRect(visibleContentRect());
596}
597
[email protected]96baf3e2012-10-22 23:09:55598void TiledLayer::resetUpdateState()
[email protected]94f206c12012-08-25 00:09:14599{
600 m_skipsDraw = false;
601 m_failedUpdate = false;
602
[email protected]96baf3e2012-10-22 23:09:55603 LayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end();
604 for (LayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != end; ++iter) {
[email protected]0def90d2012-10-15 23:16:49605 UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second);
[email protected]94f206c12012-08-25 00:09:14606 // FIXME: This should not ever be null.
607 if (!tile)
608 continue;
609 tile->resetUpdateState();
610 }
611}
612
[email protected]8d931722012-10-23 20:26:46613namespace {
[email protected]aad0a0072012-11-01 18:15:58614gfx::Rect expandRectByDelta(gfx::Rect rect, gfx::Vector2d delta) {
615 int width = rect.width() + abs(delta.x());
616 int height = rect.height() + abs(delta.y());
617 int x = rect.x() + ((delta.x() < 0) ? delta.x() : 0);
618 int y = rect.y() + ((delta.y() < 0) ? delta.y() : 0);
619 return gfx::Rect(x, y, width, height);
[email protected]8d931722012-10-23 20:26:46620}
621}
622
623void TiledLayer::updateScrollPrediction()
624{
625 // This scroll prediction is very primitive and should be replaced by a
626 // a recursive calculation on all layers which uses actual scroll/animation
627 // velocities. To insure this doesn't miss-predict, we only use it to predict
628 // the visibleRect if:
629 // - contentBounds() hasn't changed.
630 // - visibleRect.size() hasn't changed.
631 // These two conditions prevent rotations, scales, pinch-zooms etc. where
632 // the prediction would be incorrect.
[email protected]aad0a0072012-11-01 18:15:58633 gfx::Vector2d delta = visibleContentRect().CenterPoint() - m_previousVisibleRect.CenterPoint();
[email protected]8d931722012-10-23 20:26:46634 m_predictedScroll = -delta;
635 m_predictedVisibleRect = visibleContentRect();
636 if (m_previousContentBounds == contentBounds() && m_previousVisibleRect.size() == visibleContentRect().size()) {
637 // Only expand the visible rect in the major scroll direction, to prevent
638 // massive paints due to diagonal scrolls.
[email protected]aad0a0072012-11-01 18:15:58639 gfx::Vector2d majorScrollDelta = (abs(delta.x()) > abs(delta.y())) ? gfx::Vector2d(delta.x(), 0) : gfx::Vector2d(0, delta.y());
[email protected]8d931722012-10-23 20:26:46640 m_predictedVisibleRect = expandRectByDelta(visibleContentRect(), majorScrollDelta);
641
642 // Bound the prediction to prevent unbounded paints, and clamp to content bounds.
[email protected]aad0a0072012-11-01 18:15:58643 gfx::Rect bound = visibleContentRect();
644 bound.Inset(-m_tiler->tileSize().width() * maxPredictiveTilesCount,
645 -m_tiler->tileSize().height() * maxPredictiveTilesCount);
646 bound.Intersect(gfx::Rect(gfx::Point(), contentBounds()));
647 m_predictedVisibleRect.Intersect(bound);
[email protected]8d931722012-10-23 20:26:46648 }
649 m_previousContentBounds = contentBounds();
650 m_previousVisibleRect = visibleContentRect();
651}
652
[email protected]b4da2032012-10-25 21:22:55653void TiledLayer::update(ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats& stats)
[email protected]94f206c12012-08-25 00:09:14654{
[email protected]1d993172012-10-18 18:15:04655 DCHECK(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped?
[email protected]94f206c12012-08-25 00:09:14656 updateBounds();
657 if (m_tiler->hasEmptyBounds() || !drawsContent())
658 return;
659
660 bool didPaint = false;
661
662 // Animation pre-paint. If the layer is small, try to paint it all
663 // immediately whether or not it is occluded, to avoid paint/upload
664 // hiccups while it is animating.
665 if (isSmallAnimatedLayer(this)) {
666 int left, top, right, bottom;
[email protected]aad0a0072012-11-01 18:15:58667 m_tiler->contentRectToTileIndices(gfx::Rect(gfx::Point(), contentBounds()), left, top, right, bottom);
[email protected]94f206c12012-08-25 00:09:14668 updateTiles(left, top, right, bottom, queue, 0, stats, didPaint);
669 if (didPaint)
670 return;
671 // This was an attempt to paint the entire layer so if we fail it's okay,
672 // just fallback on painting visible etc. below.
673 m_failedUpdate = false;
674 }
675
[email protected]aad0a0072012-11-01 18:15:58676 if (m_predictedVisibleRect.IsEmpty())
[email protected]94f206c12012-08-25 00:09:14677 return;
678
679 // Visible painting. First occlude visible tiles and paint the non-occluded tiles.
680 int left, top, right, bottom;
[email protected]8d931722012-10-23 20:26:46681 m_tiler->contentRectToTileIndices(m_predictedVisibleRect, left, top, right, bottom);
[email protected]94f206c12012-08-25 00:09:14682 markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
683 m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint);
684 if (m_skipsDraw)
685 m_tiler->reset();
686 if (m_skipsDraw || didPaint)
687 return;
688
689 // If we have already painting everything visible. Do some pre-painting while idle.
[email protected]aad0a0072012-11-01 18:15:58690 gfx::Rect idlePaintContentRect = idlePaintRect();
691 if (idlePaintContentRect.IsEmpty())
[email protected]94f206c12012-08-25 00:09:14692 return;
693
694 // Prepaint anything that was occluded but inside the layer's visible region.
695 if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
696 return;
697
698 int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
699 m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
700
[email protected]8d931722012-10-23 20:26:46701 // Then expand outwards one row/column at a time until we find a dirty row/column
702 // to update. Increment along the major and minor scroll directions first.
[email protected]aad0a0072012-11-01 18:15:58703 gfx::Vector2d delta = -m_predictedScroll;
704 delta = gfx::Vector2d(delta.x() == 0 ? 1 : delta.x(),
705 delta.y() == 0 ? 1 : delta.y());
706 gfx::Vector2d majorDelta = (abs(delta.x()) > abs(delta.y())) ? gfx::Vector2d(delta.x(), 0) : gfx::Vector2d(0, delta.y());
707 gfx::Vector2d minorDelta = (abs(delta.x()) <= abs(delta.y())) ? gfx::Vector2d(delta.x(), 0) : gfx::Vector2d(0, delta.y());
708 gfx::Vector2d deltas[4] = {majorDelta, minorDelta, -majorDelta, -minorDelta};
[email protected]8d931722012-10-23 20:26:46709 for(int i = 0; i < 4; i++) {
[email protected]aad0a0072012-11-01 18:15:58710 if (deltas[i].y() > 0) {
[email protected]8d931722012-10-23 20:26:46711 while (bottom < prepaintBottom) {
712 ++bottom;
713 if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint)
714 return;
715 }
[email protected]94f206c12012-08-25 00:09:14716 }
[email protected]aad0a0072012-11-01 18:15:58717 if (deltas[i].y() < 0) {
[email protected]8d931722012-10-23 20:26:46718 while (top > prepaintTop) {
719 --top;
720 if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint)
721 return;
722 }
[email protected]94f206c12012-08-25 00:09:14723 }
[email protected]aad0a0072012-11-01 18:15:58724 if (deltas[i].x() < 0) {
[email protected]8d931722012-10-23 20:26:46725 while (left > prepaintLeft) {
726 --left;
727 if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint)
728 return;
729 }
[email protected]94f206c12012-08-25 00:09:14730 }
[email protected]aad0a0072012-11-01 18:15:58731 if (deltas[i].x() > 0) {
[email protected]8d931722012-10-23 20:26:46732 while (right < prepaintRight) {
733 ++right;
734 if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
735 return;
736 }
[email protected]94f206c12012-08-25 00:09:14737 }
738 }
739}
740
[email protected]96baf3e2012-10-22 23:09:55741bool TiledLayer::needsIdlePaint()
[email protected]94f206c12012-08-25 00:09:14742{
743 // Don't trigger more paints if we failed (as we'll just fail again).
[email protected]aad0a0072012-11-01 18:15:58744 if (m_failedUpdate || visibleContentRect().IsEmpty() || m_tiler->hasEmptyBounds() || !drawsContent())
[email protected]94f206c12012-08-25 00:09:14745 return false;
746
[email protected]aad0a0072012-11-01 18:15:58747 gfx::Rect idlePaintContentRect = idlePaintRect();
748 if (idlePaintContentRect.IsEmpty())
[email protected]94f206c12012-08-25 00:09:14749 return false;
750
751 int left, top, right, bottom;
752 m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom);
753
754 for (int j = top; j <= bottom; ++j) {
755 for (int i = left; i <= right; ++i) {
756 UpdatableTile* tile = tileAt(i, j);
[email protected]1d993172012-10-18 18:15:04757 DCHECK(tile); // Did setTexturePriorities get skipped?
[email protected]94f206c12012-08-25 00:09:14758 if (!tile)
759 continue;
760
[email protected]aad0a0072012-11-01 18:15:58761 bool updated = !tile->updateRect.IsEmpty();
[email protected]94f206c12012-08-25 00:09:14762 bool canAcquire = tile->managedTexture()->canAcquireBackingTexture();
763 bool dirty = tile->isDirty() || !tile->managedTexture()->haveBackingTexture();
764 if (!updated && canAcquire && dirty)
765 return true;
766 }
767 }
768 return false;
769}
770
[email protected]aad0a0072012-11-01 18:15:58771gfx::Rect TiledLayer::idlePaintRect()
[email protected]94f206c12012-08-25 00:09:14772{
773 // Don't inflate an empty rect.
[email protected]aad0a0072012-11-01 18:15:58774 if (visibleContentRect().IsEmpty())
775 return gfx::Rect();
[email protected]94f206c12012-08-25 00:09:14776
[email protected]aad0a0072012-11-01 18:15:58777 gfx::Rect prepaintRect = visibleContentRect();
778 prepaintRect.Inset(-m_tiler->tileSize().width() * prepaintColumns,
779 -m_tiler->tileSize().height() * prepaintRows);
780 gfx::Rect contentRect(gfx::Point(), contentBounds());
781 prepaintRect.Intersect(contentRect);
[email protected]94f206c12012-08-25 00:09:14782
783 return prepaintRect;
784}
785
[email protected]bc5e77c2012-11-05 20:00:49786} // namespace cc