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