[chromium] Simplify updateContentRect, removing rect parameter, refactor unit tests.
[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 "CCLayerImpl.h"
33 #include "CCLayerTreeHost.h"
34 #include "CCOverdrawMetrics.h"
35 #include "CCTextureUpdateQueue.h"
36 #include "CCTiledLayerImpl.h"
37 #include "GraphicsContext3D.h"
38 #include "Region.h"
39 #include "TextStream.h"
40 #include <wtf/CurrentTime.h>
41 #include <wtf/MathExtras.h>
42
43 using namespace std;
44 using WebKit::WebTransformationMatrix;
45
46 namespace WebCore {
47
48 class UpdatableTile : public CCLayerTilingData::Tile {
49     WTF_MAKE_NONCOPYABLE(UpdatableTile);
50 public:
51     static PassOwnPtr<UpdatableTile> create(PassOwnPtr<LayerTextureUpdater::Texture> texture)
52     {
53         return adoptPtr(new UpdatableTile(texture));
54     }
55
56     LayerTextureUpdater::Texture* texture() { return m_texture.get(); }
57     CCPrioritizedTexture* managedTexture() { return m_texture->texture(); }
58
59     bool isDirty() const { return !dirtyRect.isEmpty(); }
60
61     // Reset update state for the current frame. This should occur before painting
62     // for all layers. Since painting one layer can invalidate another layer
63     // after it has already painted, mark all non-dirty tiles as valid before painting
64     // such that invalidations during painting won't prevent them from being pushed.
65     void resetUpdateState()
66     {
67         updateRect = IntRect();
68         occluded = false;
69         partialUpdate = false;
70         validForFrame = !isDirty();
71     }
72
73     // This promises to update the tile and therefore also guarantees the tile
74     // will be valid for this frame. dirtyRect is copied into updateRect so
75     // we can continue to track re-entrant invalidations that occur during painting.
76     void markForUpdate()
77     {
78         validForFrame = true;
79         updateRect = dirtyRect;
80         dirtyRect = IntRect();
81     }
82
83     IntRect dirtyRect;
84     IntRect updateRect;
85     bool partialUpdate;
86     bool validForFrame;
87     bool occluded;
88     bool isInUseOnImpl;
89 private:
90     explicit UpdatableTile(PassOwnPtr<LayerTextureUpdater::Texture> texture)
91         : partialUpdate(false)
92         , validForFrame(false)
93         , occluded(false)
94         , isInUseOnImpl(false)
95         , m_texture(texture)
96     {
97     }
98
99     OwnPtr<LayerTextureUpdater::Texture> m_texture;
100 };
101
102 TiledLayerChromium::TiledLayerChromium()
103     : LayerChromium()
104     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
105     , m_skipsDraw(false)
106     , m_failedUpdate(false)
107     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
108     , m_tilingOption(AutoTile)
109 {
110     m_tiler = CCLayerTilingData::create(IntSize(), CCLayerTilingData::HasBorderTexels);
111 }
112
113 TiledLayerChromium::~TiledLayerChromium()
114 {
115 }
116
117 PassOwnPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
118 {
119     return CCTiledLayerImpl::create(id());
120 }
121
122 void TiledLayerChromium::updateTileSizeAndTilingOption()
123 {
124     ASSERT(layerTreeHost());
125
126     const IntSize& defaultTileSize = layerTreeHost()->settings().defaultTileSize;
127     const IntSize& maxUntiledLayerSize = layerTreeHost()->settings().maxUntiledLayerSize;
128     int layerWidth = contentBounds().width();
129     int layerHeight = contentBounds().height();
130
131     const IntSize tileSize(min(defaultTileSize.width(), layerWidth), min(defaultTileSize.height(), layerHeight));
132
133     // Tile if both dimensions large, or any one dimension large and the other
134     // extends into a second tile but the total layer area isn't larger than that
135     // of the largest possible untiled layer. This heuristic allows for long skinny layers
136     // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space but still avoids
137     // creating very large tiles.
138     const bool anyDimensionLarge = layerWidth > maxUntiledLayerSize.width() || layerHeight > maxUntiledLayerSize.height();
139     const bool anyDimensionOneTile = (layerWidth <= defaultTileSize.width() || layerHeight <= defaultTileSize.height())
140                                       && (layerWidth * layerHeight) <= (maxUntiledLayerSize.width() * maxUntiledLayerSize.height());
141     const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
142
143     bool isTiled;
144     if (m_tilingOption == AlwaysTile)
145         isTiled = true;
146     else if (m_tilingOption == NeverTile)
147         isTiled = false;
148     else
149         isTiled = autoTiled;
150
151     IntSize requestedSize = isTiled ? tileSize : contentBounds();
152     const int maxSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize;
153     IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
154     setTileSize(clampedSize);
155 }
156
157 void TiledLayerChromium::updateBounds()
158 {
159     IntSize oldBounds = m_tiler->bounds();
160     IntSize newBounds = contentBounds();
161     if (oldBounds == newBounds)
162         return;
163     m_tiler->setBounds(newBounds);
164
165     // Invalidate any areas that the new bounds exposes.
166     Region oldRegion(IntRect(IntPoint(), oldBounds));
167     Region newRegion(IntRect(IntPoint(), newBounds));
168     newRegion.subtract(oldRegion);
169     Vector<IntRect> rects = newRegion.rects();
170     for (size_t i = 0; i < rects.size(); ++i)
171         invalidateContentRect(rects[i]);
172 }
173
174 void TiledLayerChromium::setTileSize(const IntSize& size)
175 {
176     m_tiler->setTileSize(size);
177 }
178
179 void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOption borderTexelOption)
180 {
181     m_tiler->setBorderTexelOption(borderTexelOption);
182 }
183
184 bool TiledLayerChromium::drawsContent() const
185 {
186     if (!LayerChromium::drawsContent())
187         return false;
188
189     bool hasMoreThanOneTile = m_tiler->numTilesX() > 1 || m_tiler->numTilesY() > 1;
190     if (m_tilingOption == NeverTile && hasMoreThanOneTile)
191         return false;
192
193     return true;
194 }
195
196 bool TiledLayerChromium::needsContentsScale() const
197 {
198     return true;
199 }
200
201 IntSize TiledLayerChromium::contentBounds() const
202 {
203     return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds().height() * contentsScale()));
204 }
205
206 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
207 {
208     m_tilingOption = tilingOption;
209 }
210
211 void TiledLayerChromium::setIsMask(bool isMask)
212 {
213     setTilingOption(isMask ? NeverTile : AutoTile);
214 }
215
216 void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
217 {
218     LayerChromium::pushPropertiesTo(layer);
219
220     CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer);
221
222     tiledLayer->setSkipsDraw(m_skipsDraw);
223     tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA);
224     tiledLayer->setTilingData(*m_tiler);
225     Vector<UpdatableTile*> invalidTiles;
226
227     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
228         int i = iter->first.first;
229         int j = iter->first.second;
230         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
231         // FIXME: This should not ever be null.
232         if (!tile)
233             continue;
234         tile->isInUseOnImpl = false;
235         if (!tile->managedTexture()->haveBackingTexture()) {
236             invalidTiles.append(tile);
237             continue;
238         }
239         if (!tile->validForFrame)
240             continue;
241
242         tiledLayer->pushTileProperties(i, j, tile->managedTexture()->resourceId(), tile->opaqueRect());
243         tile->isInUseOnImpl = true;
244     }
245     for (Vector<UpdatableTile*>::const_iterator iter = invalidTiles.begin(); iter != invalidTiles.end(); ++iter)
246         m_tiler->takeTile((*iter)->i(), (*iter)->j());
247 }
248
249 CCPrioritizedTextureManager* TiledLayerChromium::textureManager() const
250 {
251     if (!layerTreeHost())
252         return 0;
253     return layerTreeHost()->contentsTextureManager();
254 }
255
256 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
257 {
258     if (host && host != layerTreeHost()) {
259         for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
260             UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
261             // FIXME: This should not ever be null.
262             if (!tile)
263                 continue;
264             tile->managedTexture()->setTextureManager(host->contentsTextureManager());
265         }
266     }
267     LayerChromium::setLayerTreeHost(host);
268 }
269
270 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
271 {
272     return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
273 }
274
275 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
276 {
277     createTextureUpdaterIfNeeded();
278
279     OwnPtr<UpdatableTile> tile(UpdatableTile::create(textureUpdater()->createTexture(textureManager())));
280     tile->managedTexture()->setDimensions(m_tiler->tileSize(), m_textureFormat);
281
282     UpdatableTile* addedTile = tile.get();
283     m_tiler->addTile(tile.release(), i, j);
284
285     addedTile->dirtyRect = m_tiler->tileRect(addedTile);
286
287     // Temporary diagnostic crash.
288     if (!addedTile)
289         CRASH();
290     if (!tileAt(i, j))
291         CRASH();
292
293     return addedTile;
294 }
295
296 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
297 {
298     float contentsWidthScale = static_cast<float>(contentBounds().width()) / bounds().width();
299     float contentsHeightScale = static_cast<float>(contentBounds().height()) / bounds().height();
300     FloatRect scaledDirtyRect(dirtyRect);
301     scaledDirtyRect.scale(contentsWidthScale, contentsHeightScale);
302     IntRect dirty = enclosingIntRect(scaledDirtyRect);
303     invalidateContentRect(dirty);
304     LayerChromium::setNeedsDisplayRect(dirtyRect);
305 }
306
307 void TiledLayerChromium::setUseLCDText(bool useLCDText)
308 {
309     LayerChromium::setUseLCDText(useLCDText);
310
311     CCLayerTilingData::BorderTexelOption borderTexelOption;
312 #if OS(ANDROID)
313     // Always want border texels and GL_LINEAR due to pinch zoom.
314     borderTexelOption = CCLayerTilingData::HasBorderTexels;
315 #else
316     borderTexelOption = useLCDText ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels;
317 #endif
318     setBorderTexelOption(borderTexelOption);
319 }
320
321 void TiledLayerChromium::invalidateContentRect(const IntRect& contentRect)
322 {
323     updateBounds();
324     if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw)
325         return;
326
327     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
328         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
329         ASSERT(tile);
330         // FIXME: This should not ever be null.
331         if (!tile)
332             continue;
333         IntRect bound = m_tiler->tileRect(tile);
334         bound.intersect(contentRect);
335         tile->dirtyRect.unite(bound);
336     }
337 }
338
339 // Returns true if tile is dirty and only part of it needs to be updated.
340 bool TiledLayerChromium::tileOnlyNeedsPartialUpdate(UpdatableTile* tile)
341 {
342     return !tile->dirtyRect.contains(m_tiler->tileRect(tile));
343 }
344
345 // Dirty tiles with valid textures needs buffered update to guarantee that
346 // we don't modify textures currently used for drawing by the impl thread.
347 bool TiledLayerChromium::tileNeedsBufferedUpdate(UpdatableTile* tile)
348 {
349     if (!tile->managedTexture()->haveBackingTexture())
350         return false;
351
352     if (!tile->isDirty())
353         return false;
354
355     if (!tile->isInUseOnImpl)
356         return false;
357
358     return true;
359 }
360
361
362 bool TiledLayerChromium::updateTiles(int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats, bool& didPaint)
363 {
364     didPaint = false;
365     createTextureUpdaterIfNeeded();
366
367     bool ignoreOcclusions = !occlusion;
368     if (!haveTexturesForTiles(left, top, right, bottom, ignoreOcclusions)) {
369         m_failedUpdate = true;
370         return false;
371     }
372
373     IntRect paintRect = markTilesForUpdate(left, top, right, bottom, ignoreOcclusions);
374
375     if (occlusion)
376         occlusion->overdrawMetrics().didPaint(paintRect);
377
378     if (paintRect.isEmpty())
379         return true;
380
381     didPaint = true;
382     updateTileTextures(paintRect, left, top, right, bottom, queue, occlusion, stats);
383     return true;
384 }
385
386 void TiledLayerChromium::markOcclusionsAndRequestTextures(int left, int top, int right, int bottom, const CCOcclusionTracker* occlusion)
387 {
388     // There is some difficult dependancies between occlusions, recording occlusion metrics
389     // and requesting memory so those are encapsulated in this function:
390     // - We only want to call requestLate on unoccluded textures (to preserve
391     //   memory for other layers when near OOM).
392     // - We only want to record occlusion metrics if all memory requests succeed.
393
394     int occludedTileCount = 0;
395     bool succeeded = true;
396     for (int j = top; j <= bottom; ++j) {
397         for (int i = left; i <= right; ++i) {
398             UpdatableTile* tile = tileAt(i, j);
399             ASSERT(tile); // Did setTexturePriorities get skipped?
400             // FIXME: This should not ever be null.
401             if (!tile)
402                 continue;
403             ASSERT(!tile->occluded); // Did resetUpdateState get skipped? Are we doing more than one occlusion pass?
404             IntRect visibleTileRect = intersection(m_tiler->tileBounds(i, j), visibleContentRect());
405             if (occlusion && occlusion->occluded(this, visibleTileRect)) {
406                 tile->occluded = true;
407                 occludedTileCount++;
408             } else {
409                 succeeded &= tile->managedTexture()->requestLate();
410             }
411         }
412     }
413
414     if (!succeeded)
415         return;
416
417     // FIXME: Remove the loop and just pass the count!
418     for (int i = 0; i < occludedTileCount; i++)
419         occlusion->overdrawMetrics().didCullTileForUpload();
420 }
421
422 bool TiledLayerChromium::haveTexturesForTiles(int left, int top, int right, int bottom, bool ignoreOcclusions)
423 {
424     for (int j = top; j <= bottom; ++j) {
425         for (int i = left; i <= right; ++i) {
426             UpdatableTile* tile = tileAt(i, j);
427             ASSERT(tile); // Did setTexturePriorites get skipped?
428             // FIXME: This should not ever be null.
429             if (!tile)
430                 continue;
431
432             // Ensure the entire tile is dirty if we don't have the texture.
433             if (!tile->managedTexture()->haveBackingTexture())
434                 tile->dirtyRect = m_tiler->tileRect(tile);
435
436             // If using occlusion and the visible region of the tile is occluded,
437             // don't reserve a texture or update the tile.
438             if (tile->occluded && !ignoreOcclusions)
439                 continue;
440
441             if (!tile->managedTexture()->canAcquireBackingTexture())
442                 return false;
443         }
444     }
445     return true;
446 }
447
448 IntRect TiledLayerChromium::markTilesForUpdate(int left, int top, int right, int bottom, bool ignoreOcclusions)
449 {
450     IntRect paintRect;
451     for (int j = top; j <= bottom; ++j) {
452         for (int i = left; i <= right; ++i) {
453             UpdatableTile* tile = tileAt(i, j);
454             ASSERT(tile); // Did setTexturePriorites get skipped?
455             // FIXME: This should not ever be null.
456             if (!tile)
457                 continue;
458             if (tile->occluded && !ignoreOcclusions)
459                 continue;
460             paintRect.unite(tile->dirtyRect);
461             tile->markForUpdate();
462         }
463     }
464     return paintRect;
465 }
466
467 void TiledLayerChromium::updateTileTextures(const IntRect& paintRect, int left, int top, int right, int bottom, CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
468 {
469     // The updateRect should be in layer space. So we have to convert the paintRect from content space to layer space.
470     m_updateRect = FloatRect(paintRect);
471     float widthScale = bounds().width() / static_cast<float>(contentBounds().width());
472     float heightScale = bounds().height() / static_cast<float>(contentBounds().height());
473     m_updateRect.scale(widthScale, heightScale);
474
475     // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
476     // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
477     // However, we can't free the memory backing the SkCanvas until the paint finishes,
478     // so we grab a local reference here to hold the updater alive until the paint completes.
479     RefPtr<LayerTextureUpdater> protector(textureUpdater());
480     IntRect paintedOpaqueRect;
481     textureUpdater()->prepareToUpdate(paintRect, m_tiler->tileSize(), 1 / widthScale, 1 / heightScale, paintedOpaqueRect, stats);
482
483     for (int j = top; j <= bottom; ++j) {
484         for (int i = left; i <= right; ++i) {
485             UpdatableTile* tile = tileAt(i, j);
486             ASSERT(tile); // Did setTexturePriorites get skipped?
487             // FIXME: This should not ever be null.
488             if (!tile)
489                 continue;
490
491             IntRect tileRect = m_tiler->tileBounds(i, j);
492
493             // Use updateRect as the above loop copied the dirty rect for this frame to updateRect.
494             const IntRect& dirtyRect = tile->updateRect;
495             if (dirtyRect.isEmpty())
496                 continue;
497
498             // Save what was painted opaque in the tile. Keep the old area if the paint didn't touch it, and didn't paint some
499             // other part of the tile opaque.
500             IntRect tilePaintedRect = intersection(tileRect, paintRect);
501             IntRect tilePaintedOpaqueRect = intersection(tileRect, paintedOpaqueRect);
502             if (!tilePaintedRect.isEmpty()) {
503                 IntRect paintInsideTileOpaqueRect = intersection(tile->opaqueRect(), tilePaintedRect);
504                 bool paintInsideTileOpaqueRectIsNonOpaque = !tilePaintedOpaqueRect.contains(paintInsideTileOpaqueRect);
505                 bool opaquePaintNotInsideTileOpaqueRect = !tilePaintedOpaqueRect.isEmpty() && !tile->opaqueRect().contains(tilePaintedOpaqueRect);
506
507                 if (paintInsideTileOpaqueRectIsNonOpaque || opaquePaintNotInsideTileOpaqueRect)
508                     tile->setOpaqueRect(tilePaintedOpaqueRect);
509             }
510
511             // sourceRect starts as a full-sized tile with border texels included.
512             IntRect sourceRect = m_tiler->tileRect(tile);
513             sourceRect.intersect(dirtyRect);
514             // Paint rect not guaranteed to line up on tile boundaries, so
515             // make sure that sourceRect doesn't extend outside of it.
516             sourceRect.intersect(paintRect);
517
518             tile->updateRect = sourceRect;
519
520             if (sourceRect.isEmpty())
521                 continue;
522
523             tile->texture()->prepareRect(sourceRect, stats);
524             if (occlusion)
525                 occlusion->overdrawMetrics().didUpload(WebTransformationMatrix(), sourceRect, tile->opaqueRect());
526
527             const IntPoint anchor = m_tiler->tileRect(tile).location();
528
529             // Calculate tile-space rectangle to upload into.
530             IntSize destOffset(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y());
531             if (destOffset.width() < 0)
532                 CRASH();
533             if (destOffset.height() < 0)
534                 CRASH();
535
536             // Offset from paint rectangle to this tile's dirty rectangle.
537             IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y());
538             if (paintOffset.x() < 0)
539                 CRASH();
540             if (paintOffset.y() < 0)
541                 CRASH();
542             if (paintOffset.x() + sourceRect.width() > paintRect.width())
543                 CRASH();
544             if (paintOffset.y() + sourceRect.height() > paintRect.height())
545                 CRASH();
546
547             TextureUploader::Parameters upload = { tile->texture(), sourceRect, destOffset };
548             if (tile->partialUpdate)
549                 queue.appendPartialUpload(upload);
550             else
551                 queue.appendFullUpload(upload);
552         }
553     }
554 }
555
556 namespace {
557 // This picks a small animated layer to be anything less than one viewport. This
558 // is specifically for page transitions which are viewport-sized layers. The extra
559 // 64 pixels is due to these layers being slightly larger than the viewport in some cases.
560 bool isSmallAnimatedLayer(TiledLayerChromium* layer)
561 {
562     if (!layer->drawTransformIsAnimating() && !layer->screenSpaceTransformIsAnimating())
563         return false;
564     IntSize viewportSize = layer->layerTreeHost() ? layer->layerTreeHost()->deviceViewportSize() : IntSize();
565     IntRect contentRect(IntPoint::zero(), layer->contentBounds());
566     return contentRect.width() <= viewportSize.width() + 64
567         && contentRect.height() <= viewportSize.height() + 64;
568 }
569
570 // FIXME: Remove this and make this based on distance once distance can be calculated
571 // for offscreen layers. For now, prioritize all small animated layers after 512
572 // pixels of pre-painting.
573 void setPriorityForTexture(const CCPriorityCalculator& priorityCalc,
574                            const IntRect& visibleRect,
575                            const IntRect& tileRect,
576                            bool drawsToRoot,
577                            bool isSmallAnimatedLayer,
578                            CCPrioritizedTexture* texture)
579 {
580     int priority = CCPriorityCalculator::lowestPriority();
581     if (!visibleRect.isEmpty())
582         priority = priorityCalc.priorityFromDistance(visibleRect, tileRect, drawsToRoot);
583     if (isSmallAnimatedLayer)
584         priority = CCPriorityCalculator::maxPriority(priority, priorityCalc.priorityFromDistance(512, drawsToRoot));
585     if (priority != CCPriorityCalculator::lowestPriority())
586         texture->setRequestPriority(priority);
587 }
588 }
589
590 void TiledLayerChromium::setTexturePriorities(const CCPriorityCalculator& priorityCalc)
591 {
592     updateBounds();
593     resetUpdateState();
594
595     if (m_tiler->hasEmptyBounds())
596         return;
597
598     bool drawsToRoot = !renderTarget()->parent();
599     bool smallAnimatedLayer = isSmallAnimatedLayer(this);
600
601     // Minimally create the tiles in the desired pre-paint rect.
602     IntRect createTilesRect = idlePaintRect();
603     if (!createTilesRect.isEmpty()) {
604         int left, top, right, bottom;
605         m_tiler->contentRectToTileIndices(createTilesRect, left, top, right, bottom);
606         for (int j = top; j <= bottom; ++j) {
607             for (int i = left; i <= right; ++i) {
608                 if (!tileAt(i, j))
609                     createTile(i, j);
610             }
611         }
612     }
613
614     // Also, minimally create all tiles for small animated layers and also
615     // double-buffer them since we have limited their size to be reasonable.
616     IntRect doubleBufferedRect = visibleContentRect();
617     if (smallAnimatedLayer)
618         doubleBufferedRect = IntRect(IntPoint::zero(), contentBounds());
619
620     // Create additional textures for double-buffered updates when needed.
621     // These textures must stay alive while the updated textures are incrementally
622     // uploaded, swapped atomically via pushProperties, and finally deleted
623     // after the commit is complete, after which they can be recycled.
624     if (!doubleBufferedRect.isEmpty()) {
625         int left, top, right, bottom;
626         m_tiler->contentRectToTileIndices(doubleBufferedRect, left, top, right, bottom);
627         for (int j = top; j <= bottom; ++j) {
628             for (int i = left; i <= right; ++i) {
629                 UpdatableTile* tile = tileAt(i, j);
630                 if (!tile)
631                     tile = createTile(i, j);
632                 // We need an additional texture if the tile needs a buffered-update and it's not a partial update.
633                 // FIXME: Decide if partial update should be allowed based on cost
634                 // of update. https://bugs.webkit.org/show_bug.cgi?id=77376
635                 if (!layerTreeHost() || !layerTreeHost()->bufferedUpdates() || !tileNeedsBufferedUpdate(tile))
636                     continue;
637                 if (tileOnlyNeedsPartialUpdate(tile) && layerTreeHost()->requestPartialTextureUpdate()) {
638                     tile->partialUpdate = true;
639                     continue;
640                 }
641
642                 IntRect tileRect = m_tiler->tileRect(tile);
643                 tile->dirtyRect = tileRect;
644                 LayerTextureUpdater::Texture* backBuffer = tile->texture();
645                 setPriorityForTexture(priorityCalc, visibleContentRect(), tile->dirtyRect, drawsToRoot, smallAnimatedLayer, backBuffer->texture());
646                 OwnPtr<CCPrioritizedTexture> frontBuffer = CCPrioritizedTexture::create(backBuffer->texture()->textureManager(),
647                                                                                         backBuffer->texture()->size(),
648                                                                                         backBuffer->texture()->format());
649                 // Swap backBuffer into frontBuffer and add it to delete after commit queue.
650                 backBuffer->swapTextureWith(frontBuffer);
651                 layerTreeHost()->deleteTextureAfterCommit(frontBuffer.release());
652             }
653         }
654     }
655
656     // Now update priorities on all tiles we have in the layer, no matter where they are.
657     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
658         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
659         // FIXME: This should not ever be null.
660         if (!tile)
661             continue;
662         IntRect tileRect = m_tiler->tileRect(tile);
663         setPriorityForTexture(priorityCalc, visibleContentRect(), tileRect, drawsToRoot, smallAnimatedLayer, tile->managedTexture());
664     }
665 }
666
667 Region TiledLayerChromium::visibleContentOpaqueRegion() const
668 {
669     if (m_skipsDraw)
670         return Region();
671     if (opaque())
672         return visibleContentRect();
673     return m_tiler->opaqueRegionInContentRect(visibleContentRect());
674 }
675
676 void TiledLayerChromium::resetUpdateState()
677 {
678     m_skipsDraw = false;
679     m_failedUpdate = false;
680
681     CCLayerTilingData::TileMap::const_iterator end = m_tiler->tiles().end();
682     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != end; ++iter) {
683         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
684         // FIXME: This should not ever be null.
685         if (!tile)
686             continue;
687         tile->resetUpdateState();
688     }
689 }
690
691 void TiledLayerChromium::update(CCTextureUpdateQueue& queue, const CCOcclusionTracker* occlusion, CCRenderingStats& stats)
692 {
693     ASSERT(!m_skipsDraw && !m_failedUpdate); // Did resetUpdateState get skipped?
694     updateBounds();
695     if (m_tiler->hasEmptyBounds() || !drawsContent())
696         return;
697
698     bool didPaint = false;
699
700     // Animation pre-paint. If the layer is small, try to paint it all
701     // immediately whether or not it is occluded, to avoid paint/upload
702     // hiccups while it is animating.
703     if (isSmallAnimatedLayer(this)) {
704         int left, top, right, bottom;
705         m_tiler->contentRectToTileIndices(IntRect(IntPoint::zero(), contentBounds()), left, top, right, bottom);
706         updateTiles(left, top, right, bottom, queue, 0, stats, didPaint);
707         if (didPaint)
708             return;
709         // This was an attempt to paint the entire layer so if we fail it's okay,
710         // just fallback on painting visible etc. below.
711         m_failedUpdate = false;
712     }
713
714     if (visibleContentRect().isEmpty())
715         return;
716
717     // Visible painting. First occlude visible tiles and paint the non-occluded tiles.
718     int left, top, right, bottom;
719     m_tiler->contentRectToTileIndices(visibleContentRect(), left, top, right, bottom);
720     markOcclusionsAndRequestTextures(left, top, right, bottom, occlusion);
721     m_skipsDraw = !updateTiles(left, top, right, bottom, queue, occlusion, stats, didPaint);
722     if (m_skipsDraw)
723         m_tiler->reset();
724     if (m_skipsDraw || didPaint)
725         return;
726
727     // If we have already painting everything visible. Do some pre-painting while idle.
728     IntRect idlePaintContentRect = idlePaintRect();
729     if (idlePaintContentRect.isEmpty())
730         return;
731
732     // Prepaint anything that was occluded but inside the layer's visible region.
733     if (!updateTiles(left, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
734         return;
735
736     int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
737     m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
738
739     // Then expand outwards from the visible area until we find a dirty row or column to update.
740     while (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom) {
741         if (bottom < prepaintBottom) {
742             ++bottom;
743             if (!updateTiles(left, bottom, right, bottom, queue, 0, stats, didPaint) || didPaint)
744                 return;
745         }
746         if (top > prepaintTop) {
747             --top;
748             if (!updateTiles(left, top, right, top, queue, 0, stats, didPaint) || didPaint)
749                 return;
750         }
751         if (left > prepaintLeft) {
752             --left;
753             if (!updateTiles(left, top, left, bottom, queue, 0, stats, didPaint) || didPaint)
754                 return;
755         }
756         if (right < prepaintRight) {
757             ++right;
758             if (!updateTiles(right, top, right, bottom, queue, 0, stats, didPaint) || didPaint)
759                 return;
760         }
761     }
762 }
763
764 bool TiledLayerChromium::needsIdlePaint()
765 {
766     // Don't trigger more paints if we failed (as we'll just fail again).
767     if (m_failedUpdate || visibleContentRect().isEmpty() || m_tiler->hasEmptyBounds() || !drawsContent())
768         return false;
769
770     IntRect idlePaintContentRect = idlePaintRect();
771     if (idlePaintContentRect.isEmpty())
772         return false;
773
774     int left, top, right, bottom;
775     m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom);
776
777     for (int j = top; j <= bottom; ++j) {
778         for (int i = left; i <= right; ++i) {
779             UpdatableTile* tile = tileAt(i, j);
780             ASSERT(tile); // Did setTexturePriorities get skipped?
781             if (!tile)
782                 continue;
783
784             bool updated = !tile->updateRect.isEmpty();
785             bool canAcquire = tile->managedTexture()->canAcquireBackingTexture();
786             bool dirty = tile->isDirty() || !tile->managedTexture()->haveBackingTexture();
787             if (!updated && canAcquire && dirty)
788                 return true;
789         }
790     }
791     return false;
792 }
793
794 IntRect TiledLayerChromium::idlePaintRect()
795 {
796     // Don't inflate an empty rect.
797     if (visibleContentRect().isEmpty())
798         return IntRect();
799
800     // FIXME: This can be made a lot larger now! We should increase
801     //        this slowly while insuring it doesn't cause any perf issues.
802     IntRect prepaintRect = visibleContentRect();
803     prepaintRect.inflateX(m_tiler->tileSize().width());
804     prepaintRect.inflateY(m_tiler->tileSize().height() * 2);
805     IntRect contentRect(IntPoint::zero(), contentBounds());
806     prepaintRect.intersect(contentRect);
807
808     return prepaintRect;
809 }
810
811 }
812 #endif // USE(ACCELERATED_COMPOSITING)