1 /*
2 * Copyright 2010, The Android Open Source Project
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 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "TiledPage.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #include "GLUtils.h"
32 #include "IntRect.h"
33 #include "PaintTileOperation.h"
34 #include "SkPaint.h"
35 #include "SkPaintFlagsDrawFilter.h"
36 #include "TilesManager.h"
37
38 #include <cutils/log.h>
39 #include <wtf/CurrentTime.h>
40 #include <wtf/text/CString.h>
41
42 #undef XLOGC
43 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
44
45 #ifdef DEBUG
46
47 #undef XLOG
48 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
49
50 #else
51
52 #undef XLOG
53 #define XLOG(...)
54
55 #endif // DEBUG
56
57 namespace WebCore {
58
59 using namespace android;
60
TiledPage(int id,GLWebViewState * state)61 TiledPage::TiledPage(int id, GLWebViewState* state)
62 : m_baseTiles(0)
63 , m_baseTileSize(0)
64 , m_id(id)
65 , m_scale(1)
66 , m_invScale(1)
67 , m_glWebViewState(state)
68 , m_latestPictureInval(0)
69 , m_prepare(false)
70 , m_isPrefetchPage(false)
71 {
72 m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
73 #ifdef DEBUG_COUNT
74 ClassTracker::instance()->increment("TiledPage");
75 #endif
76 }
77
updateBaseTileSize()78 void TiledPage::updateBaseTileSize()
79 {
80 // This value must be at least 1 greater than the max number of allowed
81 // textures. This is because prepare() asks for a tile before it reserves
82 // a texture for that tile. If all textures are currently in use by the
83 // page then there will be no available tile and having the extra tile
84 // ensures that this does not happen. After claiming the extra tile the call
85 // to reserveTexture() will cause some other tile in the page to lose it's
86 // texture and become available, thus ensuring that we always have at least
87 // one tile that is available.
88 int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
89 if (baseTileSize > m_baseTileSize)
90 m_baseTileSize = baseTileSize;
91 }
92
~TiledPage()93 TiledPage::~TiledPage()
94 {
95 // In order to delete the page we must ensure that none of its BaseTiles are
96 // currently painting or scheduled to be painted by the TextureGenerator
97 TilesManager::instance()->removeOperationsForPage(this);
98 delete[] m_baseTiles;
99 #ifdef DEBUG_COUNT
100 ClassTracker::instance()->decrement("TiledPage");
101 #endif
102 }
103
getBaseTile(int x,int y) const104 BaseTile* TiledPage::getBaseTile(int x, int y) const
105 {
106 // TODO: replace loop over array with HashMap indexing
107 for (int j = 0; j < m_baseTileSize; j++) {
108 BaseTile& tile = m_baseTiles[j];
109 if (tile.x() == x && tile.y() == y)
110 return &tile;
111 }
112 return 0;
113 }
114
discardTextures()115 void TiledPage::discardTextures()
116 {
117 for (int j = 0; j < m_baseTileSize; j++) {
118 BaseTile& tile = m_baseTiles[j];
119 tile.discardTextures();
120 }
121 return;
122 }
123
invalidateRect(const IntRect & inval,const unsigned int pictureCount)124 void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
125 {
126 // Given the current scale level we need to mark the appropriate tiles as dirty
127 const float invTileContentWidth = m_scale / TilesManager::tileWidth();
128 const float invTileContentHeight = m_scale / TilesManager::tileHeight();
129
130 const int firstDirtyTileX = static_cast<int>(floorf(inval.x() * invTileContentWidth));
131 const int firstDirtyTileY = static_cast<int>(floorf(inval.y() * invTileContentHeight));
132 const int lastDirtyTileX = static_cast<int>(ceilf(inval.maxX() * invTileContentWidth));
133 const int lastDirtyTileY = static_cast<int>(ceilf(inval.maxY() * invTileContentHeight));
134
135 XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
136 // We defer marking the tile as dirty until the next time we need to prepare
137 // to draw.
138 m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
139 m_invalTilesRegion.op(inval.x(), inval.y(), inval.maxX(), inval.maxY(), SkRegion::kUnion_Op);
140 m_latestPictureInval = pictureCount;
141 }
142
prepareRow(bool goingLeft,int tilesInRow,int firstTileX,int y,const SkIRect & tileBounds)143 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
144 {
145 for (int i = 0; i < tilesInRow; i++) {
146 int x = firstTileX;
147
148 // If we are goingLeft, we want to schedule the tiles starting from the
149 // right (and to the left if not). This is because tiles are appended to
150 // the list and the texture uploader goes through the set front to back.
151 if (goingLeft)
152 x += (tilesInRow - 1) - i;
153 else
154 x += i;
155
156 BaseTile* currentTile = 0;
157 BaseTile* availableTile = 0;
158 for (int j = 0; j < m_baseTileSize; j++) {
159 BaseTile& tile = m_baseTiles[j];
160 if (tile.x() == x && tile.y() == y) {
161 currentTile = &tile;
162 break;
163 }
164
165 if (!availableTile || (tile.drawCount() < availableTile->drawCount()))
166 availableTile = &tile;
167 }
168
169 if (!currentTile && availableTile) {
170 XLOG("STEALING tile %d, %d (draw count %llu) for tile %d, %d",
171 availableTile->x(), availableTile->y(), availableTile->drawCount(), x, y);
172 availableTile->discardTextures(); // don't wait for textures to be stolen
173 currentTile = availableTile;
174 }
175
176 if (!currentTile) {
177 XLOG("ERROR: No tile available for tile %d %d", x, y);
178 }
179
180 if (currentTile) {
181 currentTile->setGLWebViewState(m_glWebViewState);
182 currentTile->setPage(this);
183
184 currentTile->setContents(this, x, y, m_scale);
185
186 // TODO: move below (which is largely the same for layers / tiled
187 // page) into prepare() function
188
189 // ensure there is a texture associated with the tile and then check to
190 // see if the texture is dirty and in need of repainting
191 if (currentTile->isDirty() || !currentTile->frontTexture())
192 currentTile->reserveTexture();
193 if (currentTile->backTexture()
194 && currentTile->isDirty()
195 && !currentTile->isRepaintPending()) {
196 PaintTileOperation *operation = new PaintTileOperation(currentTile);
197 TilesManager::instance()->scheduleOperation(operation);
198 }
199 }
200 }
201 }
202
updateTileDirtiness(const SkIRect & tileBounds)203 bool TiledPage::updateTileDirtiness(const SkIRect& tileBounds)
204 {
205 if (!m_glWebViewState || tileBounds.isEmpty()) {
206 m_invalRegion.setEmpty();
207 m_invalTilesRegion.setEmpty();
208 return false;
209 }
210
211 bool visibleTileIsDirty = false;
212 for (int x = 0; x < m_baseTileSize; x++) {
213
214 BaseTile& tile = m_baseTiles[x];
215
216 // if the tile is in the dirty region then we must invalidate it
217 if (m_invalRegion.contains(tile.x(), tile.y())) {
218 tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
219 if (tileBounds.contains(tile.x(), tile.y()))
220 visibleTileIsDirty = true;
221 }
222 }
223
224 // clear the invalidated region as all tiles within that region have now
225 // been marked as dirty.
226 m_invalRegion.setEmpty();
227 m_invalTilesRegion.setEmpty();
228 return visibleTileIsDirty;
229 }
230
prepare(bool goingDown,bool goingLeft,const SkIRect & tileBounds,PrepareBounds bounds)231 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds, PrepareBounds bounds)
232 {
233 if (!m_glWebViewState)
234 return;
235
236 TilesManager::instance()->gatherTextures();
237 m_scrollingDown = goingDown;
238
239 int firstTileX = tileBounds.fLeft;
240 int firstTileY = tileBounds.fTop;
241 int nbTilesWidth = tileBounds.width();
242 int nbTilesHeight = tileBounds.height();
243
244 int lastTileX = tileBounds.fRight - 1;
245 int lastTileY = tileBounds.fBottom - 1;
246
247 // Expand number of tiles to allow tiles outside of viewport to be prepared for
248 // smoother scrolling.
249 int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
250 int nMaxTilesPerPage = m_baseTileSize / 2;
251
252 if (bounds == ExpandedBounds) {
253 // prepare tiles outside of the visible bounds
254 int expandX = m_glWebViewState->expandedTileBoundsX();
255 int expandY = m_glWebViewState->expandedTileBoundsY();
256
257 firstTileX -= expandX;
258 lastTileX += expandX;
259 nbTilesWidth += expandX * 2;
260
261 firstTileY -= expandY;
262 lastTileY += expandY;
263 nbTilesHeight += expandY * 2;
264 }
265
266 // crop the prepared region to the contents of the base layer
267 float maxWidthTiles = m_glWebViewState->baseContentWidth() * m_scale / TilesManager::tileWidth();
268 float maxHeightTiles = m_glWebViewState->baseContentHeight() * m_scale / TilesManager::tileHeight();
269 firstTileX = std::max(0, firstTileX);
270 firstTileY = std::max(0, firstTileY);
271 lastTileX = std::min(lastTileX, static_cast<int>(ceilf(maxWidthTiles)) - 1);
272 lastTileY = std::min(lastTileY, static_cast<int>(ceilf(maxHeightTiles)) - 1);
273
274 m_expandedTileBounds.fLeft = firstTileX;
275 m_expandedTileBounds.fTop = firstTileY;
276 m_expandedTileBounds.fRight = lastTileX;
277 m_expandedTileBounds.fBottom = lastTileY;
278
279 // check against corrupted scale values giving bad height/width (use float to avoid overflow)
280 float numTiles = static_cast<float>(nbTilesHeight) * static_cast<float>(nbTilesWidth);
281 if (numTiles > TilesManager::getMaxTextureAllocation() || nbTilesHeight < 1 || nbTilesWidth < 1)
282 {
283 XLOGC("ERROR: We don't have enough tiles for this page!"
284 " nbTilesHeight %d nbTilesWidth %d", nbTilesHeight, nbTilesWidth);
285 return;
286 }
287 for (int i = 0; i < nbTilesHeight; i++)
288 prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
289
290 m_prepare = true;
291 }
292
swapBuffersIfReady(const SkIRect & tileBounds,float scale,SwapMethod swap)293 bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapMethod swap)
294 {
295 if (!m_glWebViewState)
296 return false;
297
298 if (!m_invalRegion.isEmpty() && !m_prepare)
299 return false;
300
301 if (m_scale != scale)
302 return false;
303
304 int swaps = 0;
305 bool fullSwap = true;
306 if (swap == SwapWholePage) {
307 for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
308 for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
309 BaseTile* t = getBaseTile(x, y);
310 if (!t || !t->isTileReady())
311 return false;
312 }
313 }
314 } else { // SwapWhateveryIsReady
315 for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
316 for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
317 BaseTile* t = getBaseTile(x, y);
318 if (!t || !t->isTileReady())
319 fullSwap = false;
320 }
321 }
322 }
323
324 // swap every tile on page (even if off screen)
325 for (int j = 0; j < m_baseTileSize; j++) {
326 BaseTile& tile = m_baseTiles[j];
327 if (tile.swapTexturesIfNeeded())
328 swaps++;
329 }
330
331 XLOG("%p %s swapped %d textures, returning true",
332 this, (swap == SwapWholePage) ? "whole page" : "greedy swap", swaps);
333 return fullSwap;
334 }
335
336
draw(float transparency,const SkIRect & tileBounds)337 void TiledPage::draw(float transparency, const SkIRect& tileBounds)
338 {
339 if (!m_glWebViewState)
340 return;
341
342 const float tileWidth = TilesManager::tileWidth() * m_invScale;
343 const float tileHeight = TilesManager::tileHeight() * m_invScale;
344
345 SkIRect actualTileBounds = tileBounds;
346 actualTileBounds.fTop -= m_glWebViewState->expandedTileBoundsY();
347 actualTileBounds.fBottom += m_glWebViewState->expandedTileBoundsY();
348 actualTileBounds.fLeft -= m_glWebViewState->expandedTileBoundsX();
349 actualTileBounds.fRight += m_glWebViewState->expandedTileBoundsX();
350
351 actualTileBounds.fTop = std::max(0, actualTileBounds.fTop);
352 actualTileBounds.fLeft = std::max(0, actualTileBounds.fLeft);
353
354 for (int j = 0; j < m_baseTileSize; j++) {
355 BaseTile& tile = m_baseTiles[j];
356 bool tileInView = actualTileBounds.contains(tile.x(), tile.y());
357 if (tileInView) {
358 SkRect rect;
359 rect.fLeft = tile.x() * tileWidth;
360 rect.fTop = tile.y() * tileHeight;
361 rect.fRight = rect.fLeft + tileWidth;
362 rect.fBottom = rect.fTop + tileHeight;
363
364 tile.draw(transparency, rect, m_scale);
365 }
366
367 TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
368 }
369 }
370
paint(BaseTile * tile,SkCanvas * canvas,unsigned int * pictureUsed)371 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
372 {
373 // TODO: consider other flags so the pre-rendered tiles aren't so ugly
374 static SkPaintFlagsDrawFilter prefetchFilter(SkPaint::kAllFlags, 0);
375
376 if (!m_glWebViewState)
377 return false;
378
379 if (isPrefetchPage())
380 canvas->setDrawFilter(&prefetchFilter);
381
382 *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
383 return true;
384 }
385
paintExtra(SkCanvas * canvas)386 void TiledPage::paintExtra(SkCanvas* canvas)
387 {
388 }
389
sibling()390 TiledPage* TiledPage::sibling()
391 {
392 if (!m_glWebViewState)
393 return 0;
394 return m_glWebViewState->sibling(this);
395 }
396
397 } // namespace WebCore
398
399 #endif // USE(ACCELERATED_COMPOSITING)
400