a22d60d7d8639767d579520742244fdff1a12c0e
[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) : m_texture(texture) { }
57
58     LayerTextureUpdater::Texture* texture() { return m_texture.get(); }
59     ManagedTexture* managedTexture() { return m_texture->texture(); }
60
61     bool isDirty() const { return !m_dirtyLayerRect.isEmpty(); }
62     void clearDirty() { m_dirtyLayerRect = IntRect(); }
63
64     // Layer-space dirty rectangle that needs to be repainted.
65     IntRect m_dirtyLayerRect;
66     // Content-space rectangle that needs to be updated.
67     IntRect m_updateRect;
68 private:
69     OwnPtr<LayerTextureUpdater::Texture> m_texture;
70 };
71
72 TiledLayerChromium::TiledLayerChromium(CCLayerDelegate* delegate)
73     : LayerChromium(delegate)
74     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
75     , m_skipsDraw(false)
76     , m_skipsIdlePaint(false)
77     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
78     , m_tilingOption(AutoTile)
79 {
80     m_tiler = CCLayerTilingData::create(IntSize(defaultTileSize, defaultTileSize), CCLayerTilingData::HasBorderTexels);
81 }
82
83 TiledLayerChromium::~TiledLayerChromium()
84 {
85 }
86
87 PassRefPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
88 {
89     return CCTiledLayerImpl::create(id());
90 }
91
92 void TiledLayerChromium::cleanupResources()
93 {
94     LayerChromium::cleanupResources();
95
96     m_tiler->reset();
97     m_paintRect = IntRect();
98     m_requestedUpdateTilesRect = IntRect();
99 }
100
101 void TiledLayerChromium::updateTileSizeAndTilingOption()
102 {
103     const IntSize tileSize(min(defaultTileSize, contentBounds().width()), min(defaultTileSize, contentBounds().height()));
104
105     // Tile if both dimensions large, or any one dimension large and the other
106     // extends into a second tile. This heuristic allows for long skinny layers
107     // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space.
108     const bool anyDimensionLarge = contentBounds().width() > maxUntiledSize || contentBounds().height() > maxUntiledSize;
109     const bool anyDimensionOneTile = contentBounds().width() <= defaultTileSize || contentBounds().height() <= defaultTileSize;
110     const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
111
112     bool isTiled;
113     if (m_tilingOption == AlwaysTile)
114         isTiled = true;
115     else if (m_tilingOption == NeverTile)
116         isTiled = false;
117     else
118         isTiled = autoTiled;
119
120     IntSize requestedSize = isTiled ? tileSize : contentBounds();
121     const int maxSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize;
122     IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
123     setTileSize(clampedSize);
124 }
125
126 void TiledLayerChromium::updateBounds()
127 {
128     IntSize oldBounds = m_tiler->bounds();
129     IntSize newBounds = contentBounds();
130     if (oldBounds == newBounds)
131         return;
132     m_tiler->setBounds(newBounds);
133
134     // Invalidate any areas that the new bounds exposes.
135     Region oldRegion(IntRect(IntPoint(), oldBounds));
136     Region newRegion(IntRect(IntPoint(), newBounds));
137     newRegion.subtract(oldRegion);
138     Vector<IntRect> rects = newRegion.rects();
139     for (size_t i = 0; i < rects.size(); ++i)
140         invalidateRect(rects[i]);
141 }
142
143 void TiledLayerChromium::setTileSize(const IntSize& size)
144 {
145     m_tiler->setTileSize(size);
146 }
147
148 void TiledLayerChromium::setBorderTexelOption(CCLayerTilingData::BorderTexelOption borderTexelOption)
149 {
150     m_tiler->setBorderTexelOption(borderTexelOption);
151 }
152
153 bool TiledLayerChromium::drawsContent() const
154 {
155     if (!m_delegate)
156         return false;
157
158     if (m_tilingOption == NeverTile && m_tiler->numTiles() > 1)
159         return false;
160
161     return !m_skipsDraw;
162 }
163
164 bool TiledLayerChromium::needsContentsScale() const
165 {
166     return true;
167 }
168
169 IntSize TiledLayerChromium::contentBounds() const
170 {
171     return IntSize(lroundf(bounds().width() * contentsScale()), lroundf(bounds().height() * contentsScale()));
172 }
173
174 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
175 {
176     if (host == layerTreeHost())
177         return;
178
179     if (layerTreeHost())
180         cleanupResources();
181
182     LayerChromium::setLayerTreeHost(host);
183
184     if (!host)
185         return;
186
187     createTextureUpdater(host);
188     setTextureFormat(host->layerRendererCapabilities().bestTextureFormat);
189     m_sampledTexelFormat = textureUpdater()->sampledTexelFormat(m_textureFormat);
190 }
191
192 void TiledLayerChromium::updateCompositorResources(GraphicsContext3D*, CCTextureUpdater& updater)
193 {
194     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
195     if (m_skipsDraw || m_requestedUpdateTilesRect.isEmpty() || m_tiler->isEmpty())
196         return;
197
198     int left = m_requestedUpdateTilesRect.x();
199     int top = m_requestedUpdateTilesRect.y();
200     int right = m_requestedUpdateTilesRect.maxX() - 1;
201     int bottom = m_requestedUpdateTilesRect.maxY() - 1;
202     for (int j = top; j <= bottom; ++j) {
203         for (int i = left; i <= right; ++i) {
204             UpdatableTile* tile = tileAt(i, j);
205
206             // Required tiles are created in prepareToUpdate(). A tile should
207             // never be removed between the call to prepareToUpdate() and the
208             // call to updateCompositorResources().
209             if (!tile)
210                 CRASH();
211
212             IntRect sourceRect = tile->m_updateRect;
213             if (tile->m_updateRect.isEmpty())
214                 continue;
215
216             ASSERT(tile->managedTexture()->isReserved());
217             const IntPoint anchor = m_tiler->tileContentRect(tile).location();
218
219             // Calculate tile-space rectangle to upload into.
220             IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size());
221             if (destRect.x() < 0)
222                 CRASH();
223             if (destRect.y() < 0)
224                 CRASH();
225
226             // Offset from paint rectangle to this tile's dirty rectangle.
227             IntPoint paintOffset(sourceRect.x() - m_paintRect.x(), sourceRect.y() - m_paintRect.y());
228             if (paintOffset.x() < 0)
229                 CRASH();
230             if (paintOffset.y() < 0)
231                 CRASH();
232             if (paintOffset.x() + destRect.width() > m_paintRect.width())
233                 CRASH();
234             if (paintOffset.y() + destRect.height() > m_paintRect.height())
235                 CRASH();
236
237             updater.append(tile->texture(), sourceRect, destRect);
238         }
239     }
240
241     m_updateRect = FloatRect(m_tiler->contentRectToLayerRect(m_paintRect));
242 }
243
244 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
245 {
246     m_tilingOption = tilingOption;
247 }
248
249 void TiledLayerChromium::setIsMask(bool isMask)
250 {
251     setTilingOption(isMask ? NeverTile : AutoTile);
252 }
253
254 void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
255 {
256     LayerChromium::pushPropertiesTo(layer);
257
258     CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer);
259
260     tiledLayer->setSkipsDraw(m_skipsDraw);
261     tiledLayer->setContentsSwizzled(m_sampledTexelFormat != LayerTextureUpdater::SampledTexelFormatRGBA);
262     tiledLayer->setTilingData(*m_tiler);
263
264     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
265         int i = iter->first.first;
266         int j = iter->first.second;
267         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
268         if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
269             continue;
270         if (tile->isDirty())
271             continue;
272
273         tiledLayer->syncTextureId(i, j, tile->managedTexture()->textureId());
274     }
275 }
276
277 TextureManager* TiledLayerChromium::textureManager() const
278 {
279     if (!layerTreeHost())
280         return 0;
281     return layerTreeHost()->contentsTextureManager();
282 }
283
284 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
285 {
286     return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
287 }
288
289 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
290 {
291     RefPtr<UpdatableTile> tile = adoptRef(new UpdatableTile(textureUpdater()->createTexture(textureManager())));
292     m_tiler->addTile(tile, i, j);
293     tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile.get());
294
295     return tile.get();
296 }
297
298 void TiledLayerChromium::setNeedsDisplayRect(const FloatRect& dirtyRect)
299 {
300     IntRect dirty = enclosingIntRect(dirtyRect);
301     invalidateRect(dirty);
302     LayerChromium::setNeedsDisplayRect(dirtyRect);
303 }
304
305 void TiledLayerChromium::setIsNonCompositedContent(bool isNonCompositedContent)
306 {
307     LayerChromium::setIsNonCompositedContent(isNonCompositedContent);
308
309     CCLayerTilingData::BorderTexelOption borderTexelOption;
310 #if OS(ANDROID)
311     // Always want border texels and GL_LINEAR due to pinch zoom.
312     borderTexelOption = CCLayerTilingData::HasBorderTexels;
313 #else
314     borderTexelOption = isNonCompositedContent ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels;
315 #endif
316     setBorderTexelOption(borderTexelOption);
317 }
318
319 void TiledLayerChromium::invalidateRect(const IntRect& contentRect)
320 {
321     if (m_tiler->isEmpty() || contentRect.isEmpty() || m_skipsDraw)
322         return;
323
324     // Dirty rects are always in layer space, as the layer could be repositioned
325     // after invalidation.
326     const IntRect layerRect = m_tiler->contentRectToLayerRect(contentRect);
327
328     int left, top, right, bottom;
329     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
330     for (int j = top; j <= bottom; ++j) {
331         for (int i = left; i <= right; ++i) {
332             UpdatableTile* tile = tileAt(i, j);
333             if (!tile)
334                 continue;
335             IntRect bound = m_tiler->tileLayerRect(tile);
336             bound.intersect(layerRect);
337             tile->m_dirtyLayerRect.unite(bound);
338         }
339     }
340 }
341
342 void TiledLayerChromium::protectVisibleTileTextures()
343 {
344     protectTileTextures(IntRect(IntPoint::zero(), contentBounds()));
345 }
346
347 void TiledLayerChromium::protectTileTextures(const IntRect& contentRect)
348 {
349     if (m_tiler->isEmpty() || contentRect.isEmpty())
350         return;
351
352     int left, top, right, bottom;
353     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
354
355     for (int j = top; j <= bottom; ++j) {
356         for (int i = left; i <= right; ++i) {
357             UpdatableTile* tile = tileAt(i, j);
358             if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
359                 continue;
360
361             tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat);
362         }
363     }
364 }
365
366 void TiledLayerChromium::prepareToUpdateTiles(bool idle, int left, int top, int right, int bottom)
367 {
368     // Reset m_updateRect for all tiles.
369     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
370         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
371         tile->m_updateRect = IntRect();
372     }
373
374     // Create tiles as needed, expanding a dirty rect to contain all
375     // the dirty regions currently being drawn.
376     IntRect dirtyLayerRect;
377     for (int j = top; j <= bottom; ++j) {
378         for (int i = left; i <= right; ++i) {
379             UpdatableTile* tile = tileAt(i, j);
380             if (!tile)
381                 tile = createTile(i, j);
382
383             if (!tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat))
384                 tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile);
385
386             if (!tile->managedTexture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
387                 m_skipsIdlePaint = true;
388                 if (!idle) {
389                     m_skipsDraw = true;
390                     cleanupResources();
391                 }
392                 return;
393             }
394
395             dirtyLayerRect.unite(tile->m_dirtyLayerRect);
396         }
397     }
398
399     m_paintRect = m_tiler->layerRectToContentRect(dirtyLayerRect);
400     if (dirtyLayerRect.isEmpty())
401         return;
402
403     // Due to borders, when the paint rect is extended to tile boundaries, it
404     // may end up overlapping more tiles than the original content rect. Record
405     // the original tiles so we don't upload more tiles than necessary.
406     if (!m_paintRect.isEmpty())
407         m_requestedUpdateTilesRect = IntRect(left, top, right - left + 1, bottom - top + 1);
408
409     // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
410     // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
411     // However, we can't free the memory backing the GraphicsContext until the paint finishes,
412     // so we grab a local reference here to hold the updater alive until the paint completes.
413     RefPtr<LayerTextureUpdater> protector(textureUpdater());
414     textureUpdater()->prepareToUpdate(m_paintRect, m_tiler->tileSize(), m_tiler->hasBorderTexels(), contentsScale());
415     for (int j = top; j <= bottom; ++j) {
416         for (int i = left; i <= right; ++i) {
417             UpdatableTile* tile = tileAt(i, j);
418
419             // Tiles are created before prepareToUpdate() is called.
420             if (!tile)
421                 CRASH();
422
423             if (!tile->isDirty())
424                 continue;
425
426             // Calculate content-space rectangle to copy from.
427             IntRect sourceRect = m_tiler->tileContentRect(tile);
428             sourceRect.intersect(m_tiler->layerRectToContentRect(tile->m_dirtyLayerRect));
429             // Paint rect not guaranteed to line up on tile boundaries, so
430             // make sure that sourceRect doesn't extend outside of it.
431             sourceRect.intersect(m_paintRect);
432
433             // updateCompositorResources() uses m_updateRect to determine
434             // the tiles to update so we can clear the dirty rectangle here.
435             tile->clearDirty();
436
437             tile->m_updateRect = sourceRect;
438             if (sourceRect.isEmpty())
439                 continue;
440
441             tile->texture()->prepareRect(sourceRect);
442         }
443     }
444 }
445
446
447 void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
448 {
449     m_skipsDraw = false;
450     m_skipsIdlePaint = false;
451     m_requestedUpdateTilesRect = IntRect();
452     m_paintRect = IntRect();
453
454     updateBounds();
455
456     if (contentRect.isEmpty() || !m_tiler->numTiles())
457         return;
458
459     int left, top, right, bottom;
460     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
461
462     prepareToUpdateTiles(false, left, top, right, bottom);
463 }
464
465 void TiledLayerChromium::prepareToUpdateIdle(const IntRect& contentRect)
466 {
467     // Abort if we have already prepared a paint or run out of memory.
468     if (m_skipsIdlePaint || !m_paintRect.isEmpty())
469         return;
470
471     ASSERT(m_tiler);
472
473     updateBounds();
474
475     if (m_tiler->isEmpty())
476         return;
477
478     // Protect any textures in the pre-paint area so we don't end up just
479     // reclaiming them below.
480     IntRect idlePaintContentRect = idlePaintRect(contentRect);
481     protectTileTextures(idlePaintContentRect);
482
483     // Expand outwards until we find a dirty row or column to update.
484     int left, top, right, bottom;
485     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
486     int prepaintLeft, prepaintTop, prepaintRight, prepaintBottom;
487     m_tiler->contentRectToTileIndices(idlePaintContentRect, prepaintLeft, prepaintTop, prepaintRight, prepaintBottom);
488     while (!m_skipsIdlePaint && (left > prepaintLeft || top > prepaintTop || right < prepaintRight || bottom < prepaintBottom)) {
489         if (bottom < prepaintBottom) {
490             ++bottom;
491             prepareToUpdateTiles(true, left, bottom, right, bottom);
492             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
493                 break;
494         }
495         if (top > prepaintTop) {
496             --top;
497             prepareToUpdateTiles(true, left, top, right, top);
498             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
499                 break;
500         }
501         if (left > prepaintLeft) {
502             --left;
503             prepareToUpdateTiles(true, left, top, left, bottom);
504             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
505                 break;
506         }
507         if (right < prepaintRight) {
508             ++right;
509             prepareToUpdateTiles(true, right, top, right, bottom);
510             if (!m_paintRect.isEmpty() || m_skipsIdlePaint)
511                 break;
512         }
513     }
514 }
515
516 bool TiledLayerChromium::needsIdlePaint(const IntRect& contentRect)
517 {
518     if (m_skipsIdlePaint)
519         return false;
520
521     IntRect idlePaintContentRect = idlePaintRect(contentRect);
522
523     int left, top, right, bottom;
524     m_tiler->contentRectToTileIndices(idlePaintContentRect, left, top, right, bottom);
525     for (int j = top; j <= bottom; ++j) {
526         for (int i = left; i <= right; ++i) {
527             if (m_requestedUpdateTilesRect.contains(IntPoint(i, j)))
528                 continue;
529             UpdatableTile* tile = tileAt(i, j);
530             if (!tile || !tile->managedTexture()->isValid(m_tiler->tileSize(), m_textureFormat) || tile->isDirty())
531                 return true;
532         }
533     }
534     return false;
535 }
536
537 IntRect TiledLayerChromium::idlePaintRect(const IntRect& visibleContentRect)
538 {
539     IntRect prepaintRect = visibleContentRect;
540     // FIXME: This can be made a lot larger if we can:
541     // - reserve memory at a lower priority than for visible content
542     // - only reserve idle paint tiles up to a memory reclaim threshold and
543     // - insure we play nicely with other layers
544     prepaintRect.inflateX(m_tiler->tileSize().width());
545     prepaintRect.inflateY(m_tiler->tileSize().height());
546     prepaintRect.intersect(IntRect(IntPoint::zero(), contentBounds()));
547     return prepaintRect;
548 }
549
550 }
551 #endif // USE(ACCELERATED_COMPOSITING)