Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / platform / graphics / texmap / coordinated / TiledBackingStore.cpp
1 /*
2  Copyright (C) 2010-2012 Nokia Corporation and/or its subsidiary(-ies)
3  
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8  
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  Library General Public License for more details.
13  
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB.  If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "TiledBackingStore.h"
22
23 #if USE(COORDINATED_GRAPHICS)
24 #include "GraphicsContext.h"
25 #include "TiledBackingStoreClient.h"
26 #include <wtf/CheckedArithmetic.h>
27 #include <wtf/MemoryPressureHandler.h>
28
29 namespace WebCore {
30
31 static const int defaultTileDimension = 512;
32
33 static IntPoint innerBottomRight(const IntRect& rect)
34 {
35     // Actually, the rect does not contain rect.maxX(). Refer to IntRect::contain.
36     return IntPoint(rect.maxX() - 1, rect.maxY() - 1);
37 }
38
39 TiledBackingStore::TiledBackingStore(TiledBackingStoreClient* client, float contentsScale)
40     : m_client(client)
41     , m_tileSize(defaultTileDimension, defaultTileDimension)
42     , m_coverAreaMultiplier(2.0f)
43     , m_contentsScale(contentsScale)
44     , m_supportsAlpha(false)
45     , m_pendingTileCreation(false)
46 {
47 }
48
49 TiledBackingStore::~TiledBackingStore() = default;
50
51 void TiledBackingStore::setTrajectoryVector(const FloatPoint& trajectoryVector)
52 {
53     m_pendingTrajectoryVector = trajectoryVector;
54     m_pendingTrajectoryVector.normalize();
55 }
56
57 void TiledBackingStore::createTilesIfNeeded(const IntRect& unscaledVisibleRect, const IntRect& contentsRect)
58 {
59     IntRect scaledContentsRect = mapFromContents(contentsRect);
60     IntRect visibleRect = mapFromContents(unscaledVisibleRect);
61     float coverAreaMultiplier = MemoryPressureHandler::singleton().isUnderMemoryPressure() ? 1.0f : 2.0f;
62
63     bool didChange = m_trajectoryVector != m_pendingTrajectoryVector || m_visibleRect != visibleRect || m_rect != scaledContentsRect || m_coverAreaMultiplier != coverAreaMultiplier;
64     if (didChange || m_pendingTileCreation)
65         createTiles(visibleRect, scaledContentsRect, coverAreaMultiplier);
66 }
67
68 void TiledBackingStore::invalidate(const IntRect& contentsDirtyRect)
69 {
70     IntRect dirtyRect(mapFromContents(contentsDirtyRect));
71     IntRect keepRectFitToTileSize = tileRectForCoordinate(tileCoordinateForPoint(m_keepRect.location()));
72     keepRectFitToTileSize.unite(tileRectForCoordinate(tileCoordinateForPoint(innerBottomRight(m_keepRect))));
73
74     // Only iterate on the part of the rect that we know we might have tiles.
75     IntRect coveredDirtyRect = intersection(dirtyRect, keepRectFitToTileSize);
76     Tile::Coordinate topLeft = tileCoordinateForPoint(coveredDirtyRect.location());
77     Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(coveredDirtyRect));
78
79     for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
80         for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
81             Tile* currentTile = m_tiles.get(Tile::Coordinate(xCoordinate, yCoordinate));
82             if (!currentTile)
83                 continue;
84             // Pass the full rect to each tile as coveredDirtyRect might not
85             // contain them completely and we don't want partial tile redraws.
86             currentTile->invalidate(dirtyRect);
87         }
88     }
89 }
90
91 void TiledBackingStore::updateTileBuffers()
92 {
93     // FIXME: In single threaded case, tile back buffers could be updated asynchronously 
94     // one by one and then swapped to front in one go. This would minimize the time spent
95     // blocking on tile updates.
96     bool updated = false;
97     for (auto& tile : m_tiles.values()) {
98         if (!tile->isDirty())
99             continue;
100
101         updated |= tile->updateBackBuffer();
102     }
103
104     if (updated)
105         m_client->didUpdateTileBuffers();
106 }
107
108 double TiledBackingStore::tileDistance(const IntRect& viewport, const Tile::Coordinate& tileCoordinate) const
109 {
110     if (viewport.intersects(tileRectForCoordinate(tileCoordinate)))
111         return 0;
112
113     IntPoint viewCenter = viewport.location() + IntSize(viewport.width() / 2, viewport.height() / 2);
114     Tile::Coordinate centerCoordinate = tileCoordinateForPoint(viewCenter);
115
116     return std::max(abs(centerCoordinate.y() - tileCoordinate.y()), abs(centerCoordinate.x() - tileCoordinate.x()));
117 }
118
119 // Returns a ratio between 0.0f and 1.0f of the surface covered by rendered tiles.
120 float TiledBackingStore::coverageRatio(const WebCore::IntRect& dirtyRect) const
121 {
122     float rectArea = dirtyRect.width() * dirtyRect.height();
123     float coverArea = 0.0f;
124
125     Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location());
126     Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(dirtyRect));
127
128     for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
129         for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
130             Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate);
131             Tile* currentTile = m_tiles.get(currentCoordinate);
132             if (currentTile && currentTile->isReadyToPaint()) {
133                 IntRect coverRect = intersection(dirtyRect, currentTile->rect());
134                 coverArea += coverRect.width() * coverRect.height();
135             }
136         }
137     }
138     return coverArea / rectArea;
139 }
140
141 bool TiledBackingStore::visibleAreaIsCovered() const
142 {
143     return coverageRatio(intersection(m_visibleRect, m_rect)) == 1.0f;
144 }
145
146 void TiledBackingStore::createTiles(const IntRect& visibleRect, const IntRect& scaledContentsRect, float coverAreaMultiplier)
147 {
148     // Update our backing store geometry.
149     const IntRect previousRect = m_rect;
150     m_rect = scaledContentsRect;
151     m_trajectoryVector = m_pendingTrajectoryVector;
152     m_visibleRect = visibleRect;
153     m_coverAreaMultiplier = coverAreaMultiplier;
154
155     if (m_rect.isEmpty()) {
156         setCoverRect(IntRect());
157         setKeepRect(IntRect());
158         return;
159     }
160
161     /* We must compute cover and keep rects using the visibleRect, instead of the rect intersecting the visibleRect with m_rect,
162      * because TBS can be used as a backing store of GraphicsLayer and the visible rect usually does not intersect with m_rect.
163      * In the below case, the intersecting rect is an empty.
164      *
165      *  +---------------+
166      *  |               |
167      *  |   m_rect      |
168      *  |       +-------|-----------------------+
169      *  |       | HERE  |  cover or keep        |
170      *  +---------------+      rect             |
171      *          |         +---------+           |
172      *          |         | visible |           |
173      *          |         |  rect   |           |
174      *          |         +---------+           |
175      *          |                               |
176      *          |                               |
177      *          +-------------------------------+
178      *
179      * We must create or keep the tiles in the HERE region.
180      */
181
182     IntRect coverRect;
183     IntRect keepRect;
184     computeCoverAndKeepRect(m_visibleRect, coverRect, keepRect);
185
186     setCoverRect(coverRect);
187     setKeepRect(keepRect);
188
189     if (coverRect.isEmpty())
190         return;
191
192     // Resize tiles at the edge in case the contents size has changed, but only do so
193     // after having dropped tiles outside the keep rect.
194     bool didResizeTiles = false;
195     if (previousRect != m_rect)
196         didResizeTiles = resizeEdgeTiles();
197
198     // Search for the tile position closest to the viewport center that does not yet contain a tile.
199     // Which position is considered the closest depends on the tileDistance function.
200     double shortestDistance = std::numeric_limits<double>::infinity();
201     Vector<Tile::Coordinate> tilesToCreate;
202     unsigned requiredTileCount = 0;
203
204     // Cover areas (in tiles) with minimum distance from the visible rect. If the visible rect is
205     // not covered already it will be covered first in one go, due to the distance being 0 for tiles
206     // inside the visible rect.
207     Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.location());
208     Tile::Coordinate bottomRight = tileCoordinateForPoint(innerBottomRight(coverRect));
209     for (int yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) {
210         for (int xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) {
211             Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate);
212             if (m_tiles.contains(currentCoordinate))
213                 continue;
214             ++requiredTileCount;
215             double distance = tileDistance(m_visibleRect, currentCoordinate);
216             if (distance > shortestDistance)
217                 continue;
218             if (distance < shortestDistance) {
219                 tilesToCreate.clear();
220                 shortestDistance = distance;
221             }
222             tilesToCreate.append(currentCoordinate);
223         }
224     }
225
226     // Now construct the tile(s) within the shortest distance.
227     unsigned tilesToCreateCount = tilesToCreate.size();
228     for (unsigned n = 0; n < tilesToCreateCount; ++n) {
229         Tile::Coordinate coordinate = tilesToCreate[n];
230         m_tiles.add(coordinate, std::make_unique<Tile>(*this, coordinate));
231     }
232     requiredTileCount -= tilesToCreateCount;
233
234     // Paint the content of the newly created tiles or resized tiles.
235     if (tilesToCreateCount || didResizeTiles)
236         updateTileBuffers();
237
238     // Re-call createTiles on a timer to cover the visible area with the newest shortest distance.
239     m_pendingTileCreation = requiredTileCount;
240     if (m_pendingTileCreation)
241         m_client->tiledBackingStoreHasPendingTileCreation();
242 }
243
244 void TiledBackingStore::adjustForContentsRect(IntRect& rect) const
245 {
246     IntRect bounds = m_rect;
247     IntSize candidateSize = rect.size();
248
249     rect.intersect(bounds);
250
251     if (rect.size() == candidateSize)
252         return;
253
254     /*
255      * In the following case, there is no intersection of the contents rect and the cover rect.
256      * Thus the latter should not be inflated.
257      *
258      *  +---------------+
259      *  |   m_rect      |
260      *  +---------------+
261      *
262      *          +-------------------------------+
263      *          |          cover rect           |
264      *          |         +---------+           |
265      *          |         | visible |           |
266      *          |         |  rect   |           |
267      *          |         +---------+           |
268      *          +-------------------------------+
269      */
270     if (rect.isEmpty())
271         return;
272
273     // Try to create a cover rect of the same size as the candidate, but within content bounds.
274     int pixelsCovered = 0;
275     if (!WTF::safeMultiply(candidateSize.width(), candidateSize.height(), pixelsCovered))
276         pixelsCovered = std::numeric_limits<int>::max();
277
278     if (rect.width() < candidateSize.width())
279         rect.inflateY(((pixelsCovered / rect.width()) - rect.height()) / 2);
280     if (rect.height() < candidateSize.height())
281         rect.inflateX(((pixelsCovered / rect.height()) - rect.width()) / 2);
282
283     rect.intersect(bounds);
284 }
285
286 void TiledBackingStore::computeCoverAndKeepRect(const IntRect& visibleRect, IntRect& coverRect, IntRect& keepRect) const
287 {
288     coverRect = visibleRect;
289     keepRect = visibleRect;
290
291     // If we cover more that the actual viewport we can be smart about which tiles we choose to render.
292     if (m_coverAreaMultiplier > 1) {
293         // The initial cover area covers equally in each direction, according to the coverAreaMultiplier.
294         coverRect.inflateX(visibleRect.width() * (m_coverAreaMultiplier - 1) / 2);
295         coverRect.inflateY(visibleRect.height() * (m_coverAreaMultiplier - 1) / 2);
296         keepRect = coverRect;
297
298         if (m_trajectoryVector != FloatPoint::zero()) {
299             // A null trajectory vector (no motion) means that tiles for the coverArea will be created.
300             // A non-null trajectory vector will shrink the covered rect to visibleRect plus its expansion from its
301             // center toward the cover area edges in the direction of the given vector.
302
303             // E.g. if visibleRect == (10,10)5x5 and coverAreaMultiplier == 3.0:
304             // a (0,0) trajectory vector will create tiles intersecting (5,5)15x15,
305             // a (1,0) trajectory vector will create tiles intersecting (10,10)10x5,
306             // and a (1,1) trajectory vector will create tiles intersecting (10,10)10x10.
307
308             // Multiply the vector by the distance to the edge of the cover area.
309             float trajectoryVectorMultiplier = (m_coverAreaMultiplier - 1) / 2;
310
311             // Unite the visible rect with a "ghost" of the visible rect moved in the direction of the trajectory vector.
312             coverRect = visibleRect;
313             coverRect.move(coverRect.width() * m_trajectoryVector.x() * trajectoryVectorMultiplier, coverRect.height() * m_trajectoryVector.y() * trajectoryVectorMultiplier);
314
315             coverRect.unite(visibleRect);
316         }
317         ASSERT(keepRect.contains(coverRect));
318     }
319
320     adjustForContentsRect(coverRect);
321
322     // The keep rect is an inflated version of the cover rect, inflated in tile dimensions.
323     keepRect.unite(coverRect);
324     keepRect.inflateX(m_tileSize.width() / 2);
325     keepRect.inflateY(m_tileSize.height() / 2);
326     keepRect.intersect(m_rect);
327
328     ASSERT(coverRect.isEmpty() || keepRect.contains(coverRect));
329 }
330
331 bool TiledBackingStore::resizeEdgeTiles()
332 {
333     bool wasResized = false;
334     Vector<Tile::Coordinate> tilesToRemove;
335     for (auto& tile : m_tiles.values()) {
336         Tile::Coordinate tileCoordinate = tile->coordinate();
337         IntRect tileRect = tile->rect();
338         IntRect expectedTileRect = tileRectForCoordinate(tileCoordinate);
339         if (expectedTileRect.isEmpty())
340             tilesToRemove.append(tileCoordinate);
341         else if (expectedTileRect != tileRect) {
342             tile->resize(expectedTileRect.size());
343             wasResized = true;
344         }
345     }
346
347     for (auto& coordinateToRemove : tilesToRemove)
348         m_tiles.remove(coordinateToRemove);
349
350     return wasResized;
351 }
352
353 void TiledBackingStore::setKeepRect(const IntRect& keepRect)
354 {
355     // Drop tiles outside the new keepRect.
356
357     FloatRect keepRectF = keepRect;
358
359     Vector<Tile::Coordinate> toRemove;
360     for (auto& tile : m_tiles.values()) {
361         Tile::Coordinate coordinate = tile->coordinate();
362         FloatRect tileRect = tile->rect();
363         if (!tileRect.intersects(keepRectF))
364             toRemove.append(coordinate);
365     }
366
367     for (auto& coordinateToRemove : toRemove)
368         m_tiles.remove(coordinateToRemove);
369
370     m_keepRect = keepRect;
371 }
372
373 void TiledBackingStore::removeAllNonVisibleTiles(const IntRect& unscaledVisibleRect, const IntRect& contentsRect)
374 {
375     IntRect boundedVisibleRect = mapFromContents(intersection(unscaledVisibleRect, contentsRect));
376     setKeepRect(boundedVisibleRect);
377 }
378
379 IntRect TiledBackingStore::mapToContents(const IntRect& rect) const
380 {
381     return enclosingIntRect(FloatRect(rect.x() / m_contentsScale,
382         rect.y() / m_contentsScale,
383         rect.width() / m_contentsScale,
384         rect.height() / m_contentsScale));
385 }
386
387 IntRect TiledBackingStore::mapFromContents(const IntRect& rect) const
388 {
389     return enclosingIntRect(FloatRect(rect.x() * m_contentsScale,
390         rect.y() * m_contentsScale,
391         rect.width() * m_contentsScale,
392         rect.height() * m_contentsScale));
393 }
394
395 IntRect TiledBackingStore::tileRectForCoordinate(const Tile::Coordinate& coordinate) const
396 {
397     IntRect rect(coordinate.x() * m_tileSize.width(),
398         coordinate.y() * m_tileSize.height(),
399         m_tileSize.width(),
400         m_tileSize.height());
401
402     rect.intersect(m_rect);
403     return rect;
404 }
405
406 Tile::Coordinate TiledBackingStore::tileCoordinateForPoint(const IntPoint& point) const
407 {
408     int x = point.x() / m_tileSize.width();
409     int y = point.y() / m_tileSize.height();
410     return Tile::Coordinate(std::max(x, 0), std::max(y, 0));
411 }
412
413 void TiledBackingStore::setSupportsAlpha(bool a)
414 {
415     if (a == m_supportsAlpha)
416         return;
417     m_supportsAlpha = a;
418     invalidate(m_rect);
419 }
420
421 }
422
423 #endif