OSDN Git Service

Don't prepare offscreen tiles
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / android / TiledPage.cpp
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
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     , m_willDraw(false)
72 {
73     m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
74 #ifdef DEBUG_COUNT
75     ClassTracker::instance()->increment("TiledPage");
76 #endif
77 }
78
79 void TiledPage::updateBaseTileSize()
80 {
81     // This value must be at least 1 greater than the max number of allowed
82     // textures. This is because prepare() asks for a tile before it reserves
83     // a texture for that tile. If all textures are currently in use by the
84     // page then there will be no available tile and having the extra tile
85     // ensures that this does not happen. After claiming the extra tile the call
86     // to reserveTexture() will cause some other tile in the page to lose it's
87     // texture and become available, thus ensuring that we always have at least
88     // one tile that is available.
89     int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
90     if (baseTileSize > m_baseTileSize)
91         m_baseTileSize = baseTileSize;
92 }
93
94 TiledPage::~TiledPage()
95 {
96     // In order to delete the page we must ensure that none of its BaseTiles are
97     // currently painting or scheduled to be painted by the TextureGenerator
98     TilesManager::instance()->removeOperationsForPage(this);
99     delete[] m_baseTiles;
100 #ifdef DEBUG_COUNT
101     ClassTracker::instance()->decrement("TiledPage");
102 #endif
103 }
104
105 BaseTile* TiledPage::getBaseTile(int x, int y) const
106 {
107     // TODO: replace loop over array with HashMap indexing
108     for (int j = 0; j < m_baseTileSize; j++) {
109         BaseTile& tile = m_baseTiles[j];
110         if (tile.x() == x && tile.y() == y)
111             return &tile;
112     }
113     return 0;
114 }
115
116 void TiledPage::discardTextures()
117 {
118     for (int j = 0; j < m_baseTileSize; j++) {
119         BaseTile& tile = m_baseTiles[j];
120         tile.discardTextures();
121     }
122     return;
123 }
124
125 void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
126 {
127     // Given the current scale level we need to mark the appropriate tiles as dirty
128     const float invTileContentWidth = m_scale / TilesManager::tileWidth();
129     const float invTileContentHeight = m_scale / TilesManager::tileHeight();
130
131     const int firstDirtyTileX = static_cast<int>(floorf(inval.x() * invTileContentWidth));
132     const int firstDirtyTileY = static_cast<int>(floorf(inval.y() * invTileContentHeight));
133     const int lastDirtyTileX = static_cast<int>(ceilf(inval.maxX() * invTileContentWidth));
134     const int lastDirtyTileY = static_cast<int>(ceilf(inval.maxY() * invTileContentHeight));
135
136     XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
137     // We defer marking the tile as dirty until the next time we need to prepare
138     // to draw.
139     m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
140     m_invalTilesRegion.op(inval.x(), inval.y(), inval.maxX(), inval.maxY(), SkRegion::kUnion_Op);
141     m_latestPictureInval = pictureCount;
142 }
143
144 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
145 {
146     for (int i = 0; i < tilesInRow; i++) {
147         int x = firstTileX;
148
149         // If we are goingLeft, we want to schedule the tiles starting from the
150         // right (and to the left if not). This is because tiles are appended to
151         // the list and the texture uploader goes through the set front to back.
152         if (goingLeft)
153             x += (tilesInRow - 1) - i;
154         else
155             x += i;
156
157         BaseTile* currentTile = 0;
158         BaseTile* availableTile = 0;
159         for (int j = 0; j < m_baseTileSize; j++) {
160             BaseTile& tile = m_baseTiles[j];
161             if (tile.x() == x && tile.y() == y) {
162                 currentTile = &tile;
163                 break;
164             }
165
166             if (!availableTile || (tile.drawCount() < availableTile->drawCount()))
167                 availableTile = &tile;
168         }
169
170         if (!currentTile && availableTile) {
171             XLOG("STEALING tile %d, %d (draw count %llu) for tile %d, %d",
172                   availableTile->x(), availableTile->y(), availableTile->drawCount(), x, y);
173             availableTile->discardTextures(); // don't wait for textures to be stolen
174             currentTile = availableTile;
175         }
176
177         if (!currentTile) {
178             XLOG("ERROR: No tile available for tile %d %d", x, y);
179         }
180
181         if (currentTile) {
182             currentTile->setGLWebViewState(m_glWebViewState);
183             currentTile->setPage(this);
184
185             currentTile->setContents(this, x, y, m_scale);
186
187             // TODO: move below (which is largely the same for layers / tiled
188             // page) into prepare() function
189
190             // ensure there is a texture associated with the tile and then check to
191             // see if the texture is dirty and in need of repainting
192             if (currentTile->isDirty() || !currentTile->frontTexture())
193                 currentTile->reserveTexture();
194             if (currentTile->backTexture()
195                     && currentTile->isDirty()
196                     && !currentTile->isRepaintPending()) {
197                 PaintTileOperation *operation = new PaintTileOperation(currentTile);
198                 TilesManager::instance()->scheduleOperation(operation);
199             }
200         }
201     }
202 }
203
204 bool TiledPage::updateTileDirtiness(const SkIRect& tileBounds)
205 {
206     if (!m_glWebViewState || tileBounds.isEmpty()) {
207         m_invalRegion.setEmpty();
208         m_invalTilesRegion.setEmpty();
209         return false;
210     }
211
212     bool visibleTileIsDirty = false;
213     for (int x = 0; x < m_baseTileSize; x++) {
214
215         BaseTile& tile = m_baseTiles[x];
216
217         // if the tile is in the dirty region then we must invalidate it
218         if (m_invalRegion.contains(tile.x(), tile.y())) {
219             tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
220             if (tileBounds.contains(tile.x(), tile.y()))
221                 visibleTileIsDirty = true;
222         }
223     }
224
225     // clear the invalidated region as all tiles within that region have now
226     // been marked as dirty.
227     m_invalRegion.setEmpty();
228     m_invalTilesRegion.setEmpty();
229     return visibleTileIsDirty;
230 }
231
232 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds, PrepareBounds bounds)
233 {
234     if (!m_glWebViewState)
235         return;
236
237     TilesManager::instance()->gatherTextures();
238     m_scrollingDown = goingDown;
239
240     int firstTileX = tileBounds.fLeft;
241     int firstTileY = tileBounds.fTop;
242     int nbTilesWidth = tileBounds.width();
243     int nbTilesHeight = tileBounds.height();
244
245     // Expand number of tiles to allow tiles outside of viewport to be prepared for
246     // smoother scrolling.
247     int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
248     int nMaxTilesPerPage = m_baseTileSize / 2;
249
250     if (bounds == ExpandedBounds) {
251         // prepare tiles outside of the visible bounds
252         int expandX = m_glWebViewState->expandedTileBoundsX();
253         int expandY = m_glWebViewState->expandedTileBoundsY();
254
255         firstTileX -= expandX;
256         nbTilesWidth += expandX * 2;
257
258         firstTileY -= expandY;
259         nbTilesHeight += expandY * 2;
260     }
261
262     // crop the prepared region to the contents of the base layer
263     float maxWidthTiles = m_glWebViewState->baseContentWidth() * m_scale / TilesManager::tileWidth();
264     float maxHeightTiles = m_glWebViewState->baseContentHeight() * m_scale / TilesManager::tileHeight();
265
266     // adjust perimeter to not go outside base content bounds
267     if (firstTileX < 0) {
268         nbTilesWidth += firstTileX;
269         firstTileX = 0;
270     }
271     if (firstTileY < 0) {
272         nbTilesHeight += firstTileY;
273         firstTileY = 0;
274     }
275     nbTilesWidth = std::min(nbTilesWidth, static_cast<int>(ceilf(maxWidthTiles)) - firstTileX);
276     nbTilesHeight = std::min(nbTilesHeight, static_cast<int>(ceilf(maxHeightTiles)) - firstTileY);
277
278     // check against corrupted scale values giving bad height/width (use float to avoid overflow)
279     float numTiles = static_cast<float>(nbTilesHeight) * static_cast<float>(nbTilesWidth);
280     if (numTiles > TilesManager::getMaxTextureAllocation() || nbTilesHeight < 1 || nbTilesWidth < 1)
281     {
282         XLOGC("ERROR: We don't have enough tiles for this page!"
283               " nbTilesHeight %d nbTilesWidth %d", nbTilesHeight, nbTilesWidth);
284         return;
285     }
286     for (int i = 0; i < nbTilesHeight; i++)
287         prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
288
289     m_prepare = true;
290 }
291
292 bool TiledPage::hasMissingContent(const SkIRect& tileBounds)
293 {
294     int neededTiles = tileBounds.width() * tileBounds.height();
295     for (int j = 0; j < m_baseTileSize; j++) {
296         BaseTile& tile = m_baseTiles[j];
297         if (tileBounds.contains(tile.x(), tile.y())) {
298             if (tile.frontTexture())
299                 neededTiles--;
300         }
301     }
302     return neededTiles > 0;
303 }
304
305 bool TiledPage::isReady(const SkIRect& tileBounds)
306 {
307     int neededTiles = tileBounds.width() * tileBounds.height();
308     XLOG("tiled page %p needs %d ready tiles", this, neededTiles);
309     for (int j = 0; j < m_baseTileSize; j++) {
310         BaseTile& tile = m_baseTiles[j];
311         if (tileBounds.contains(tile.x(), tile.y())) {
312             if (tile.isTileReady())
313                 neededTiles--;
314         }
315     }
316     XLOG("tiled page %p still needs %d ready tiles", this, neededTiles);
317     return neededTiles == 0;
318 }
319
320 bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale)
321 {
322     if (!m_glWebViewState)
323         return false;
324
325     if (!m_invalRegion.isEmpty() && !m_prepare)
326         return false;
327
328     if (m_scale != scale)
329         return false;
330
331     int swaps = 0;
332     bool fullSwap = true;
333     for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
334         for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
335             BaseTile* t = getBaseTile(x, y);
336             if (!t || !t->isTileReady())
337                 fullSwap = false;
338         }
339     }
340
341     // swap every tile on page (even if off screen)
342     for (int j = 0; j < m_baseTileSize; j++) {
343         BaseTile& tile = m_baseTiles[j];
344         if (tile.swapTexturesIfNeeded())
345             swaps++;
346     }
347
348     XLOG("%p greedy swapped %d textures, returning true", this, swaps);
349     return fullSwap;
350 }
351
352 void TiledPage::prepareForDrawGL(float transparency, const SkIRect& tileBounds)
353 {
354     m_willDraw = true;
355     m_transparency = transparency;
356     m_tileBounds = tileBounds;
357 }
358
359 void TiledPage::drawGL()
360 {
361     if (!m_glWebViewState || m_transparency == 0 || !m_willDraw)
362         return;
363
364     const float tileWidth = TilesManager::tileWidth() * m_invScale;
365     const float tileHeight = TilesManager::tileHeight() * m_invScale;
366
367     for (int j = 0; j < m_baseTileSize; j++) {
368         BaseTile& tile = m_baseTiles[j];
369         bool tileInView = m_tileBounds.contains(tile.x(), tile.y());
370         if (tileInView) {
371             SkRect rect;
372             rect.fLeft = tile.x() * tileWidth;
373             rect.fTop = tile.y() * tileHeight;
374             rect.fRight = rect.fLeft + tileWidth;
375             rect.fBottom = rect.fTop + tileHeight;
376
377             tile.draw(m_transparency, rect, m_scale);
378         }
379
380         TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
381     }
382     m_willDraw = false; // don't redraw until re-prepared
383 }
384
385 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
386 {
387     static SkPaintFlagsDrawFilter prefetchFilter(SkPaint::kAllFlags,
388                                                  SkPaint::kAntiAlias_Flag);
389
390     if (!m_glWebViewState)
391         return false;
392
393     if (isPrefetchPage())
394         canvas->setDrawFilter(&prefetchFilter);
395
396     *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
397     return true;
398 }
399
400 TiledPage* TiledPage::sibling()
401 {
402     if (!m_glWebViewState)
403         return 0;
404     return m_glWebViewState->sibling(this);
405 }
406
407 } // namespace WebCore
408
409 #endif // USE(ACCELERATED_COMPOSITING)