[Chromium] TiledLayerChromium::protectVisibleTileTextures() should only protect the...
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / TiledLayerChromium.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if USE(ACCELERATED_COMPOSITING)
29
30 #include "TiledLayerChromium.h"
31
32 #include "GraphicsContext3D.h"
33 #include "LayerRendererChromium.h"
34 #include "ManagedTexture.h"
35 #include "MathExtras.h"
36 #include "Region.h"
37 #include "TextStream.h"
38 #include "cc/CCLayerImpl.h"
39 #include "cc/CCTextureUpdater.h"
40 #include "cc/CCTiledLayerImpl.h"
41 #include <wtf/CurrentTime.h>
42
43 // Start tiling when the width and height of a layer are larger than this size.
44 static int maxUntiledSize = 512;
45
46 // When tiling is enabled, use tiles of this dimension squared.
47 static int defaultTileSize = 256;
48
49 using namespace std;
50
51 namespace WebCore {
52
53 class UpdatableTile : public CCLayerTilingData::Tile {
54     WTF_MAKE_NONCOPYABLE(UpdatableTile);
55 public:
56     explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture)
57         : m_partialUpdate(false)
58         , m_texture(texture)
59     {
60     }
61
62     LayerTextureUpdater::Texture* texture() { return m_texture.get(); }
63     ManagedTexture* managedTexture() { return m_texture->texture(); }
64
65     bool isDirty() const { return !m_dirtyRect.isEmpty(); }
66     void copyAndClearDirty()
67     {
68         m_updateRect = m_dirtyRect;
69         m_dirtyRect = IntRect();
70     }
71
72     IntRect m_dirtyRect;
73     IntRect m_updateRect;
74     IntRect m_opaqueRect;
75     bool m_partialUpdate;
76 private:
77     OwnPtr<LayerTextureUpdater::Texture> m_texture;
78 };
79
80 TiledLayerChromium::TiledLayerChromium()
81     : LayerChromium()
82     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
83     , m_skipsDraw(false)
84     , m_skipsIdlePaint(false)
85     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
86     , m_tilingOption(AutoTile)
87 {
88     m_tiler = CCLayerTilingData::create(IntSize(defaultTileSize, defaultTileSize), CCLayerTilingData::HasBorderTexels);
89 }
90
91 TiledLayerChromium::~TiledLayerChromium()
92 {
93 }
94
95 PassRefPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
96 {
97     return CCTiledLayerImpl::create(id());
98 }
99
100 void TiledLayerChromium::updateTileSizeAndTilingOption()
101 {
102     const IntSize tileSize(min(defaultTileSize, contentBounds().width()), min(defaultTileSize, contentBounds().height()));
103
104     // Tile if both dimensions large, or any one dimension large and the other
105     // extends into a second tile. This heuristic allows for long skinny layers
106     // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space.
107     const bool anyDimensionLarge = contentBounds().width() > maxUntiledSize || contentBounds().height() > maxUntiledSize;
108     const bool anyDimensionOneTile = contentBounds().width() <= defaultTileSize || contentBounds().height() <= defaultTileSize;
109     const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
110
111     bool isTiled;
112     if (m_tilingOption == AlwaysTile)
113         isTiled = true;
114     else if (m_tilingOption == NeverTile)
115         isTiled = false;
116     else
117         isTiled = autoTiled;
118
119     IntSize requestedSize = isTiled ? tileSize : contentBounds();
120     const int maxSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize;
121     IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
122     setTileSize(clampedSize);
123 }
124
125 void TiledLayerChromium::updateBounds()
126 {
127     IntSize oldBounds = m_tiler->bounds();
128     IntSize newBounds = contentBounds();
129     if (oldBounds == newBounds)
130         return;
131     m_tiler->setBounds(newBounds);
132
133     // Invalidate any areas that the new bounds exposes.
134     Region oldRegion(IntRect(IntPoint(), oldBounds));
135     Region newRegion(IntRect(IntPoint(), newBounds));
136     newRegion.subtract(oldRegion);
137     Vector<IntRect> rects = newRegion.rects();
138     for (size_t i = 0; i < rects.size(); ++i)
139         invalidateRect(rects[i]);
140 }
141
142 void TiledLayerChromium::setTileSize(const IntSize& size)
143 {
144     m_tiler->setTileSize(size);
145 }
146
147 void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOption borderTexelOption)
148 {
149     m_tiler->setBorderTexelOption(borderTexelOption);
150 }
151
152 bool TiledLayerChromium::drawsContent() const
153 {
154     if (!LayerChromium::drawsContent())
155         return false;
156
157     if (m_tilingOption == NeverTile && m_tiler->numTiles() > 1)
158         return false;
159
160     return true;
161 }
162
163 bool TiledLayerChromium::needsContentsScale() const
164 {
165     return true;
166 }
167
168 IntSize TiledLayerChromium::contentBounds() const
169 {
170     return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds().height() * contentsScale()));
171 }
172
173 void TiledLayerChromium::updateCompositorResources(GraphicsContext3D*, CCTextureUpdater& updater)
174 {
175     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
176     if (m_skipsDraw || m_requestedUpdateTilesRect.isEmpty() || m_tiler->isEmpty())
177         return;
178
179     int left = m_requestedUpdateTilesRect.x();
180     int top = m_requestedUpdateTilesRect.y();
181     int right = m_requestedUpdateTilesRect.maxX() - 1;
182     int bottom = m_requestedUpdateTilesRect.maxY() - 1;
183     for (int j = top; j <= bottom; ++j) {
184         for (int i = left; i <= right; ++i) {
185             UpdatableTile* tile = tileAt(i, j);
186
187             // Required tiles are created in prepareToUpdate(). A tile should
188             // never be removed between the call to prepareToUpdate() and the
189             // call to updateCompositorResources().
190             if (!tile)
191                 CRASH();
192
193             IntRect sourceRect = tile->m_updateRect;
194             if (tile->m_updateRect.isEmpty())
195                 continue;
196
197             ASSERT(tile->managedTexture()->isReserved());
198             const IntPoint anchor = m_tiler->tileRect(tile).location();
199
200             // Calculate tile-space rectangle to upload into.
201             IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size());
202             if (destRect.x() < 0)
203                 CRASH();
204             if (destRect.y() < 0)
205                 CRASH();
206
207             // Offset from paint rectangle to this tile's dirty rectangle.
208             IntPoint paintOffset(sourceRect.x() - m_paintRect.x(), sourceRect.y() - m_paintRect.y());
209             if (paintOffset.x() < 0)
210                 CRASH();
211             if (paintOffset.y() < 0)
212                 CRASH();
213             if (paintOffset.x() + destRect.width() > m_paintRect.width())
214                 CRASH();
215             if (paintOffset.y() + destRect.height() > m_paintRect.height())
216                 CRASH();
217
218             if (tile->m_partialUpdate)
219                 updater.appendPartial(tile->texture(), sourceRect, destRect);
220             else
221                 updater.append(tile->texture(), sourceRect, destRect);
222         }
223     }
224
225     // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space.
226     m_updateRect = FloatRect(m_paintRect);
227     float widthScale = bounds().width() / static_cast<float>(contentBounds().width());
228     float heightScale = bounds().height() / static_cast<float>(contentBounds().height());
229     m_updateRect.scale(widthScale, heightScale);
230 }
231
232 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
233 {
234     m_tilingOption = tilingOption;
235 }
236
237 void TiledLayerChromium::setIsMask(bool isMask)
238 {
239     setTilingOption(isMask ? NeverTile : AutoTile);
240 }
241
242 void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
243 {
244     LayerChromium::pushPropertiesTo(layer);
245
246     CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer);
247
248     tiledLayer->setSkipsDraw(m_skipsDraw);
249     tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA);
250     tiledLayer->setTilingData(*m_tiler);
251     Vector<UpdatableTile*> invalidTiles;
252
253     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
254         int i = iter->first.first;
255         int j = iter->first.second;
256         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
257         if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat)) {
258             invalidTiles.append(tile);
259             continue;
260         }
261         if (tile->isDirty())
262             continue;
263
264         tiledLayer->pushTileProperties(i, j, tile->managedTexture()->textureId(), tile->m_opaqueRect);
265     }
266     for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter)
267         m_tiler->takeTile((*iter)->i(), (*iter)->j());
268 }
269
270 TextureManager* TiledLayerChromium::textureManager() const
271 {
272     if (!layerTreeHost())
273         return 0;
274     return layerTreeHost()->contentsTextureManager();
275 }
276
277 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
278 {
279     if (host && host != layerTreeHost()) {
280         for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
281             UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
282             tile->managedTexture()->setTextureManager(host->contentsTextureManager());
283         }
284     }
285     LayerChromium::setLayerTreeHost(host);
286 }
287
288 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
289 {
290     return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
291 }
292
293 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
294 {
295     RefPtr<UpdatableTile> tile = adoptRef(new UpdatableTile(textureUpdater()->createTexture(textureManager())));
296     m_tiler->addTile(tile, i, j);
297     tile->m_dirtyRect = m_tiler->tileRect(tile.get());
298
299     return tile.get();
300 }
301
302 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
303 {
304     IntRect dirty = enclosingIntRect(dirtyRect);
305     invalidateRect(dirty);
306     LayerChromium::setNeedsDisplayRect(dirtyRect);
307 }
308
309 void TiledLayerChromium::setIsNonCompositedContent(bool isNonCompositedContent)
310 {
311     LayerChromium::setIsNonCompositedContent(isNonCompositedContent);
312
313     CCLayerTilingData::BorderTexelOption borderTexelOption;
314 #if OS(ANDROID)
315     // Always want border texels and GL_LINEAR due to pinch zoom.
316     borderTexelOption = CCLayerTilingData::HasBorderTexels;
317 #else
318     borderTexelOption = isNonCompositedContent ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels;
319 #endif
320     setBorderTexelOption(borderTexelOption);
321 }
322
323 void TiledLayerChromium::invalidateRect(const IntRect& layerRect)
324 {
325     updateBounds();
326     if (m_tiler->isEmpty() || layerRect.isEmpty() || m_skipsDraw)
327         return;
328
329     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
330         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
331         ASSERT(tile);
332         IntRect bound = m_tiler->tileRect(tile);
333         bound.intersect(layerRect);
334         tile->m_dirtyRect.unite(bound);
335     }
336 }
337
338 void TiledLayerChromium::protectVisibleTileTextures()
339 {
340     protectTileTextures(visibleLayerRect());
341 }
342
343 void TiledLayerChromium::protectTileTextures(const IntRect& layerRect)
344 {
345     if (m_tiler->isEmpty() || layerRect.isEmpty())
346         return;
347
348     int left, top, right, bottom;
349     m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
350
351     for (int j = top; j <= bottom; ++j) {
352         for (int i = left; i <= right; ++i) {
353             UpdatableTile* tile = tileAt(i, j);
354             if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
355                 continue;
356
357             tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat);
358         }
359     }
360 }
361
362 // Returns true if tile is dirty and only part of it needs to be updated.
363 bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
364 {
365     if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
366         return false;
367
368     if (!tile->isDirty())
369         return false;
370
371     return !tile->m_dirtyRect.contains(m_tiler->tileRect(tile));
372 }
373
374 // Dirty tiles with valid textures needs buffered update to guarantee that
375 // we don't modify textures currently used for drawing by the impl thread.
376 bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile)
377 {
378     // No impl thread?.
379     if (!CCProxy::hasImplThread())
380         return false;
381
382     if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
383         return false;
384
385     if (!tile->isDirty())
386         return false;
387
388     return true;
389 }
390
391 void TiledLayerChromium::prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom)
392 {
393     // Reset m_updateRect for all tiles.
394     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
395         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
396         tile->m_updateRect = IntRect();
397         tile->m_partialUpdate = false;
398     }
399
400     createTextureUpdaterIfNeeded();
401
402     // Create tiles as needed, expanding a dirty rect to contain all
403     // the dirty regions currently being drawn. All dirty tiles that are to be painted
404     // get their m_updateRect set to m_dirtyRect and m_dirtyRect cleared. This way if
405     // invalidateRect is invoked during prepareToUpdate we don't lose the request.
406     IntRect dirtyLayerRect;
407     for (int j = top; j <= bottom; ++j) {
408         for (int i = left; i <= right; ++i) {
409             UpdatableTile* tile = tileAt(i, j);
410             if (!tile)
411                 tile = createTile(i, j);
412
413             // 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 (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost() && layerTreeHost()->requestPartialTextureUpdate())
416                 tile->m_partialUpdate = true;
417             else if (tileNeedsBufferedUpdate(tile) && layerTreeHost())
418                 layerTreeHost()->deleteTextureAfterCommit(tile->managedTexture()->steal());
419
420             if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
421                 tile->m_dirtyRect = m_tiler->tileRect(tile);
422
423             if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
424                 m_skipsIdlePaint = true;
425                 if (!idle) {
426                     // If the background covers the viewport, always draw this
427                     // layer so that checkerboarded tiles will still draw.
428                     if (!backgroundCoversViewport())
429                         m_skipsDraw = true;
430                     m_tiler->reset();
431                     m_paintRect = IntRect();
432                     m_requestedUpdateTilesRect = IntRect();
433                 }
434                 return;
435             }
436
437             dirtyLayerRect.unite(tile->m_dirtyRect);
438             tile->copyAndClearDirty();
439         }
440     }
441
442     m_paintRect = dirtyLayerRect;
443     if (dirtyLayerRect.isEmpty())
444         return;
445
446     // Due to borders, when the paint rect is extended to tile boundaries, it
447     // may end up overlapping more tiles than the original content rect. Record
448     // the original tiles so we don't upload more tiles than necessary.
449     if (!m_paintRect.isEmpty())
450         m_requestedUpdateTilesRect = IntRect(left, top, right - left + 1, bottom - top + 1);
451
452     // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
453     // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
454     // However, we can't free the memory backing the GraphicsContext until the paint finishes,
455     // so we grab a local reference here to hold the updater alive until the paint completes.
456     RefPtr<LayerTextureUpdater> protector(textureUpdater());
457     IntRect paintedOpaqueRect;
458     textureUpdater()->prepareToUpdate(m_paintRect, m_tiler->tileSize(), m_tiler->hasBorderTexels(), contentsScale(), &paintedOpaqueRect);
459     for (int j = top; j <= bottom; ++j) {
460         for (int i = left; i <= right; ++i) {
461             UpdatableTile* tile = tileAt(i, j);
462
463             // Tiles are created before prepareToUpdate() is called.
464             if (!tile)
465                 CRASH();
466
467             IntRect tileRect = m_tiler->tileBounds(i, j);
468
469             // Save what was painted opaque in the tile. If everything painted in the tile was opaque, and the area is a subset of an
470             // already opaque area, keep the old area.
471             IntRect tilePaintedRect = intersection(tileRect, m_paintRect);
472             IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaqueRect);
473             if (tilePaintedOpaqueRect != tilePaintedRect || !tile->m_opaqueRect.contains(tilePaintedOpaqueRect))
474                 tile->m_opaqueRect = tilePaintedOpaqueRect;
475
476             // Use m_updateRect as copyAndClearDirty above moved the existing dirty rect to m_updateRect.
477             const IntRect& dirtyRect = tile->m_updateRect;
478             if (dirtyRect.isEmpty())
479                 continue;
480
481             // sourceRect starts as a full-sized tile with border texels included.
482             IntRect sourceRect = m_tiler->tileRect(tile);
483             sourceRect.intersect(dirtyRect);
484             // Paint rect not guaranteed to line up on tile boundaries, so
485             // make sure that sourceRect doesn't extend outside of it.
486             sourceRect.intersect(m_paintRect);
487
488             tile->m_updateRect = sourceRect;
489             if (sourceRect.isEmpty())
490                 continue;
491
492             tile->texture()->prepareRect(sourceRect);
493         }
494     }
495 }
496
497 void TiledLayerChromium::reserveTextures()
498 {
499     updateBounds();
500
501     const IntRect& layerRect = visibleLayerRect();
502     if (layerRect.isEmpty() || !m_tiler->numTiles())
503         return;
504
505     int left, top, right, bottom;
506     m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
507
508     createTextureUpdaterIfNeeded();
509     for (int j = top; j <= bottom; ++j) {
510         for (int i = left; i <= right; ++i) {
511             UpdatableTile* tile = tileAt(i, j);
512             if (!tile)
513                 tile = createTile(i, j);
514
515             if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
516                 tile->m_dirtyRect = m_tiler->tileRect(tile);
517
518             if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat))
519                 return;
520         }
521     }
522 }
523
524 void TiledLayerChromium::addSelfToOccludedScreenSpace(Region& occludedScreenSpace)
525 {
526     if (m_skipsDraw || drawOpacity() != 1 || !isPaintedAxisAlignedInScreen())
527         return;
528
529     if (opaque()) {
530         LayerChromium::addSelfToOccludedScreenSpace(occludedScreenSpace);
531         return;
532     }
533
534     IntRect visibleRect = visibleLayerRect();
535     TransformationMatrix contentTransform = contentToScreenSpaceTransform();
536
537     // FIXME: Create/Use a FloatRegion for the occludedScreenSpace, instead of a Region based on ints, to avoid this step and get better accuracy between layers in target space.
538     Region tileRegion;
539     int left, top, right, bottom;
540     m_tiler->layerRectToTileIndices(visibleLayerRect(), left, top, right, bottom);
541     for (int j = top; j <= bottom; ++j) {
542         for (int i = left; i <= right; ++i) {
543             UpdatableTile* tile = tileAt(i, j);
544             if (tile) {
545                 IntRect visibleTileOpaqueRect = intersection(visibleRect, tile->m_opaqueRect);
546                 FloatRect screenRect = contentTransform.mapRect(FloatRect(visibleTileOpaqueRect));
547                 occludedScreenSpace.unite(enclosedIntRect(screenRect));
548             }
549         }
550     }
551 }
552
553 void TiledLayerChromium::prepareToUpdate(const IntRect& layerRect)
554 {
555     m_skipsDraw = false;
556     m_skipsIdlePaint = false;
557     m_requestedUpdateTilesRect = IntRect();
558     m_paintRect = IntRect();
559
560     updateBounds();
561
562     if (layerRect.isEmpty() || !m_tiler->numTiles())
563         return;
564
565     int left, top, right, bottom;
566     m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
567
568     prepareToUpdateTiles(false, left, top, right, bottom);
569 }
570
571 void TiledLayerChromium::prepareToUpdateIdle(const IntRect& layerRect)
572 {
573     // Abort if we have already prepared a paint or run out of memory.
574     if (m_skipsIdlePaint || !m_paintRect.isEmpty())
575         return;
576
577     ASSERT(m_tiler);
578
579     updateBounds();
580
581     if (m_tiler->isEmpty())
582         return;
583
584     // Protect any textures in the pre-paint area so we don't end up just
585     // reclaiming them below.
586     IntRect idlePaintLayerRect = idlePaintRect(layerRect);
587     protectTileTextures(idlePaintLayerRect);
588
589     // Expand outwards until we find a dirty row or column to update.
590     int left, top, right, bottom;
591     m_tiler->layerRectToTileIndices(layerRect, left, top, right, bottom);
592     int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
593     m_tiler->layerRectToTileIndices(idlePaintLayerRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
594     while (!m_skipsIdlePaint && (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom)) {
595         if (bottom < prepaintBottom) {
596             ++bottom;
597             prepareToUpdateTiles(true, left, bottom, right, bottom);
598             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
599                 break;
600         }
601         if (top > prepaintTop) {
602             --top;
603             prepareToUpdateTiles(true, left, top, right, top);
604             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
605                 break;
606         }
607         if (left > prepaintLeft) {
608             --left;
609             prepareToUpdateTiles(true, left, top, left, bottom);
610             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
611                 break;
612         }
613         if (right < prepaintRight) {
614             ++right;
615             prepareToUpdateTiles(true, right, top, right, bottom);
616             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
617                 break;
618         }
619     }
620 }
621
622 bool TiledLayerChromium::needsIdlePaint(const IntRect& layerRect)
623 {
624     if (m_skipsIdlePaint)
625         return false;
626
627     IntRect idlePaintLayerRect = idlePaintRect(layerRect);
628
629     int left, top, right, bottom;
630     m_tiler->layerRectToTileIndices(idlePaintLayerRect, left, top, right, bottom);
631     for (int j = top; j <= bottom; ++j) {
632         for (int i = left; i <= right; ++i) {
633             if (m_requestedUpdateTilesRect.contains(IntPoint(i, j)))
634                 continue;
635             UpdatableTile* tile = tileAt(i, j);
636             if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat) || tile->isDirty())
637                 return true;
638         }
639     }
640     return false;
641 }
642
643 IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleLayerRect)
644 {
645     IntRect prepaintRect = visibleLayerRect;
646     // FIXME: This can be made a lot larger if we can:
647     // - reserve memory at a lower priority than for visible content
648     // - only reserve idle paint tiles up to a memory reclaim threshold and
649     // - insure we play nicely with other layers
650     prepaintRect.inflateX(m_tiler->tileSize().width());
651     prepaintRect.inflateY(m_tiler->tileSize().height());
652     prepaintRect.intersect(IntRect(IntPoint::zero(), contentBounds()));
653     return prepaintRect;
654 }
655
656 }
657 #endif // USE(ACCELERATED_COMPOSITING)