42693eacf8b10c8f3edf821352390a5fdab59fe3
[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 "TextStream.h"
36 #include "cc/CCLayerImpl.h"
37 #include "cc/CCTiledLayerImpl.h"
38 #include <wtf/CurrentTime.h>
39
40 // Start tiling when the width and height of a layer are larger than this size.
41 static int maxUntiledSize = 512;
42
43 // When tiling is enabled, use tiles of this dimension squared.
44 static int defaultTileSize = 256;
45
46 using namespace std;
47
48 namespace WebCore {
49
50 class UpdatableTile : public CCLayerTilingData::Tile {
51     WTF_MAKE_NONCOPYABLE(UpdatableTile);
52 public:
53     explicit UpdatableTile(PassOwnPtr<ManagedTexture> tex) : m_tex(tex) { }
54
55     ManagedTexture* texture() { return m_tex.get(); }
56
57     bool dirty() const { return !m_dirtyLayerRect.isEmpty(); }
58     void clearDirty() { m_dirtyLayerRect = IntRect(); }
59
60     // Layer-space dirty rectangle that needs to be repainted.
61     IntRect m_dirtyLayerRect;
62 private:
63     OwnPtr<ManagedTexture> m_tex;
64 };
65
66 TiledLayerChromium::TiledLayerChromium(CCLayerDelegate* delegate)
67     : LayerChromium(delegate)
68     , m_tilingOption(AutoTile)
69     , m_textureFormat(GraphicsContext3D::INVALID_ENUM)
70     , m_skipsDraw(false)
71     , m_textureOrientation(LayerTextureUpdater::InvalidOrientation)
72     , m_sampledTexelFormat(LayerTextureUpdater::SampledTexelFormatInvalid)
73 {
74 }
75
76 TiledLayerChromium::~TiledLayerChromium()
77 {
78 }
79
80 PassRefPtr<CCLayerImpl> TiledLayerChromium::createCCLayerImpl()
81 {
82     return CCTiledLayerImpl::create(id());
83 }
84
85 void TiledLayerChromium::cleanupResources()
86 {
87     LayerChromium::cleanupResources();
88
89     m_tiler.clear();
90     m_unusedTiles.clear();
91     m_paintRect = IntRect();
92     m_updateRect = IntRect();
93 }
94
95 void TiledLayerChromium::updateTileSizeAndTilingOption()
96 {
97     if (!m_tiler)
98         return;
99
100     const IntSize tileSize(min(defaultTileSize, contentBounds().width()), min(defaultTileSize, contentBounds().height()));
101
102     // Tile if both dimensions large, or any one dimension large and the other
103     // extends into a second tile. This heuristic allows for long skinny layers
104     // (e.g. scrollbars) that are Nx1 tiles to minimize wasted texture space.
105     const bool anyDimensionLarge = contentBounds().width() > maxUntiledSize || contentBounds().height() > maxUntiledSize;
106     const bool anyDimensionOneTile = contentBounds().width() <= defaultTileSize || contentBounds().height() <= defaultTileSize;
107     const bool autoTiled = anyDimensionLarge && !anyDimensionOneTile;
108
109     bool isTiled;
110     if (m_tilingOption == AlwaysTile)
111         isTiled = true;
112     else if (m_tilingOption == NeverTile)
113         isTiled = false;
114     else
115         isTiled = autoTiled;
116
117     IntSize requestedSize = isTiled ? tileSize : contentBounds();
118     const int maxSize = layerTreeHost()->layerRendererCapabilities().maxTextureSize;
119     IntSize clampedSize = requestedSize.shrunkTo(IntSize(maxSize, maxSize));
120     m_tiler->setTileSize(clampedSize);
121 }
122
123 bool TiledLayerChromium::drawsContent() const
124 {
125     if (!m_delegate)
126         return false;
127
128     if (!m_tiler)
129         return true;
130
131     if (m_tilingOption == NeverTile && m_tiler->numTiles() > 1)
132         return false;
133
134     return !m_skipsDraw;
135 }
136
137 void TiledLayerChromium::setLayerTreeHost(CCLayerTreeHost* host)
138 {
139     LayerChromium::setLayerTreeHost(host);
140
141     if (m_tiler || !host)
142         return;
143
144     createTextureUpdater(host);
145
146     m_textureFormat = host->layerRendererCapabilities().bestTextureFormat;
147     m_textureOrientation = textureUpdater()->orientation();
148     m_sampledTexelFormat = textureUpdater()->sampledTexelFormat(m_textureFormat);
149     m_tiler = CCLayerTilingData::create(
150         IntSize(defaultTileSize, defaultTileSize),
151         isNonCompositedContent() ? CCLayerTilingData::NoBorderTexels : CCLayerTilingData::HasBorderTexels);
152 }
153
154 void TiledLayerChromium::updateCompositorResources(GraphicsContext3D* context)
155 {
156     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
157     if (m_skipsDraw || m_updateRect.isEmpty() || !m_tiler->numTiles())
158         return;
159
160     int left, top, right, bottom;
161     m_tiler->contentRectToTileIndices(m_updateRect, left, top, right, bottom);
162     for (int j = top; j <= bottom; ++j) {
163         for (int i = left; i <= right; ++i) {
164             UpdatableTile* tile = tileAt(i, j);
165             if (!tile)
166                 tile = createTile(i, j);
167             else if (!tile->dirty())
168                 continue;
169
170             // Calculate page-space rectangle to copy from.
171             IntRect sourceRect = m_tiler->tileContentRect(tile);
172             const IntPoint anchor = sourceRect.location();
173             sourceRect.intersect(m_tiler->layerRectToContentRect(tile->m_dirtyLayerRect));
174             // Paint rect not guaranteed to line up on tile boundaries, so
175             // make sure that sourceRect doesn't extend outside of it.
176             sourceRect.intersect(m_paintRect);
177             if (sourceRect.isEmpty())
178                 continue;
179
180             ASSERT(tile->texture()->isReserved());
181
182             // Calculate tile-space rectangle to upload into.
183             IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size());
184             if (destRect.x() < 0)
185                 CRASH();
186             if (destRect.y() < 0)
187                 CRASH();
188
189             // Offset from paint rectangle to this tile's dirty rectangle.
190             IntPoint paintOffset(sourceRect.x() - m_paintRect.x(), sourceRect.y() - m_paintRect.y());
191             if (paintOffset.x() < 0)
192                 CRASH();
193             if (paintOffset.y() < 0)
194                 CRASH();
195             if (paintOffset.x() + destRect.width() > m_paintRect.width())
196                 CRASH();
197             if (paintOffset.y() + destRect.height() > m_paintRect.height())
198                 CRASH();
199
200             tile->texture()->bindTexture(context);
201             const GC3Dint filter = m_tiler->hasBorderTexels() ? GraphicsContext3D::LINEAR : GraphicsContext3D::NEAREST;
202             GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, filter));
203             GLC(context, context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, filter));
204             GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0));
205
206             textureUpdater()->updateTextureRect(context, tile->texture(), sourceRect, destRect);
207             tile->clearDirty();
208         }
209     }
210 }
211
212 void TiledLayerChromium::setTilingOption(TilingOption tilingOption)
213 {
214     m_tilingOption = tilingOption;
215     updateTileSizeAndTilingOption();
216 }
217
218 void TiledLayerChromium::setIsMask(bool isMask)
219 {
220     setTilingOption(isMask ? NeverTile : AutoTile);
221 }
222
223 TransformationMatrix TiledLayerChromium::tilingTransform() const
224 {
225     TransformationMatrix transform = drawTransform();
226
227     if (contentBounds().isEmpty())
228         return transform;
229
230     transform.scaleNonUniform(bounds().width() / static_cast<double>(contentBounds().width()),
231                               bounds().height() / static_cast<double>(contentBounds().height()));
232
233     // Tiler draws with a different origin from other layers.
234     transform.translate(-contentBounds().width() / 2.0, -contentBounds().height() / 2.0);
235
236     transform.translate(-scrollPosition().x(), -scrollPosition().y());
237
238     return transform;
239 }
240
241 void TiledLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
242 {
243     LayerChromium::pushPropertiesTo(layer);
244
245     CCTiledLayerImpl* tiledLayer = static_cast<CCTiledLayerImpl*>(layer);
246     if (!m_tiler) {
247         tiledLayer->setSkipsDraw(true);
248         return;
249     }
250
251     tiledLayer->setTilingTransform(tilingTransform());
252     tiledLayer->setSkipsDraw(m_skipsDraw);
253     tiledLayer->setTextureOrientation(m_textureOrientation);
254     tiledLayer->setSampledTexelFormat(m_sampledTexelFormat);
255     tiledLayer->setTilingData(*m_tiler);
256
257     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
258         int i = iter->first.first;
259         int j = iter->first.second;
260         UpdatableTile* tile = static_cast<UpdatableTile*>(iter->second.get());
261         if (!tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
262             continue;
263
264         tiledLayer->syncTextureId(i, j, tile->texture()->textureId());
265     }
266 }
267
268 TextureManager* TiledLayerChromium::textureManager() const
269 {
270     if (!layerTreeHost())
271         return 0;
272     return layerTreeHost()->contentsTextureManager();
273 }
274
275 UpdatableTile* TiledLayerChromium::tileAt(int i, int j) const
276 {
277     return static_cast<UpdatableTile*>(m_tiler->tileAt(i, j));
278 }
279
280 UpdatableTile* TiledLayerChromium::createTile(int i, int j)
281 {
282     RefPtr<UpdatableTile> tile;
283     if (m_unusedTiles.size() > 0) {
284         tile = m_unusedTiles.last().release();
285         m_unusedTiles.removeLast();
286         ASSERT(tile->refCount() == 1);
287     } else {
288         TextureManager* manager = textureManager();
289         tile = adoptRef(new UpdatableTile(ManagedTexture::create(manager)));
290     }
291     m_tiler->addTile(tile, i, j);
292     tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile.get());
293
294     return tile.get();
295 }
296
297 void TiledLayerChromium::invalidateTiles(const IntRect& contentRect)
298 {
299     if (!m_tiler->numTiles())
300         return;
301
302     Vector<CCLayerTilingData::TileMapKey> removeKeys;
303     for (CCLayerTilingData::TileMap::const_iterator iter = m_tiler->tiles().begin(); iter != m_tiler->tiles().end(); ++iter) {
304         CCLayerTilingData::Tile* tile = iter->second.get();
305         IntRect tileRect = m_tiler->tileContentRect(tile);
306         if (tileRect.intersects(contentRect))
307             continue;
308         removeKeys.append(iter->first);
309     }
310
311     for (size_t i = 0; i < removeKeys.size(); ++i)
312         m_unusedTiles.append(static_pointer_cast<UpdatableTile>(m_tiler->takeTile(removeKeys[i].first, removeKeys[i].second)));
313 }
314
315 void TiledLayerChromium::invalidateRect(const IntRect& contentRect)
316 {
317     if (!m_tiler || contentRect.isEmpty() || m_skipsDraw)
318         return;
319
320     m_tiler->growLayerToContain(contentRect);
321
322     // Dirty rects are always in layer space, as the layer could be repositioned
323     // after invalidation.
324     const IntRect layerRect = m_tiler->contentRectToLayerRect(contentRect);
325
326     int left, top, right, bottom;
327     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
328     for (int j = top; j <= bottom; ++j) {
329         for (int i = left; i <= right; ++i) {
330             UpdatableTile* tile = tileAt(i, j);
331             if (!tile)
332                 continue;
333             IntRect bound = m_tiler->tileLayerRect(tile);
334             bound.intersect(layerRect);
335             tile->m_dirtyLayerRect.unite(bound);
336         }
337     }
338 }
339
340 void TiledLayerChromium::protectVisibleTileTextures()
341 {
342     protectTileTextures(IntRect(IntPoint::zero(), contentBounds()));
343 }
344
345 void TiledLayerChromium::protectTileTextures(const IntRect& contentRect)
346 {
347     if (!m_tiler || contentRect.isEmpty())
348         return;
349
350     int left, top, right, bottom;
351     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
352
353     for (int j = top; j <= bottom; ++j) {
354         for (int i = left; i <= right; ++i) {
355             UpdatableTile* tile = tileAt(i, j);
356             if (!tile || !tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
357                 continue;
358
359             tile->texture()->reserve(m_tiler->tileSize(), m_textureFormat);
360         }
361     }
362 }
363
364 void TiledLayerChromium::prepareToUpdate(const IntRect& contentRect)
365 {
366     ASSERT(m_tiler);
367
368     m_skipsDraw = false;
369
370     if (contentRect.isEmpty()) {
371         m_updateRect = IntRect();
372         return;
373     }
374
375     // Invalidate old tiles that were previously used but aren't in use this
376     // frame so that they can get reused for new tiles.
377     invalidateTiles(contentRect);
378     m_tiler->growLayerToContain(contentRect);
379
380     if (!m_tiler->numTiles()) {
381         m_updateRect = IntRect();
382         return;
383     }
384
385     // Create tiles as needed, expanding a dirty rect to contain all
386     // the dirty regions currently being drawn.
387     IntRect dirtyLayerRect;
388     int left, top, right, bottom;
389     m_tiler->contentRectToTileIndices(contentRect, left, top, right, bottom);
390     for (int j = top; j <= bottom; ++j) {
391         for (int i = left; i <= right; ++i) {
392             UpdatableTile* tile = tileAt(i, j);
393             if (!tile)
394                 tile = createTile(i, j);
395
396             if (!tile->texture()->isValid(m_tiler->tileSize(), m_textureFormat))
397                 tile->m_dirtyLayerRect = m_tiler->tileLayerRect(tile);
398
399             if (!tile->texture()->reserve(m_tiler->tileSize(), m_textureFormat)) {
400                 m_skipsDraw = true;
401                 cleanupResources();
402                 return;
403             }
404
405             dirtyLayerRect.unite(tile->m_dirtyLayerRect);
406         }
407     }
408
409     // Due to borders, when the paint rect is extended to tile boundaries, it
410     // may end up overlapping more tiles than the original content rect. Record
411     // that original rect so we don't upload more tiles than necessary.
412     m_updateRect = contentRect;
413
414     m_paintRect = m_tiler->layerRectToContentRect(dirtyLayerRect);
415     if (dirtyLayerRect.isEmpty())
416         return;
417
418     // Calling prepareToUpdate() calls into WebKit to paint, which may have the side
419     // effect of disabling compositing, which causes our reference to the texture updater to be deleted.
420     // However, we can't free the memory backing the GraphicsContext until the paint finishes,
421     // so we grab a local reference here to hold the updater alive until the paint completes.
422     RefPtr<LayerTextureUpdater> protector(textureUpdater());
423     textureUpdater()->prepareToUpdate(m_paintRect, m_tiler->tileSize(), m_tiler->hasBorderTexels());
424 }
425
426 }
427 #endif // USE(ACCELERATED_COMPOSITING)