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