OSDN Git Service

34f0d4ee8a5664ea201f2d38895d42dbb50088a3
[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 {
72     m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
73 #ifdef DEBUG_COUNT
74     ClassTracker::instance()->increment("TiledPage");
75 #endif
76 }
77
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
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
104 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
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
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
143 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
144 {
145     if (y < 0)
146         return;
147
148     for (int i = 0; i < tilesInRow; i++) {
149         int x = firstTileX;
150
151         // If we are goingLeft, we want to schedule the tiles starting from the
152         // right (and to the left if not). This is because tiles are appended to
153         // the list and the texture uploader goes through the set front to back.
154         if (goingLeft)
155             x += (tilesInRow - 1) - i;
156         else
157             x += i;
158
159         if (x < 0)
160             continue;
161
162         BaseTile* currentTile = 0;
163         BaseTile* availableTile = 0;
164         for (int j = 0; j < m_baseTileSize; j++) {
165             BaseTile& tile = m_baseTiles[j];
166             if (tile.x() == x && tile.y() == y) {
167                 currentTile = &tile;
168                 break;
169             }
170
171             if (!availableTile || (tile.drawCount() < availableTile->drawCount()))
172                 availableTile = &tile;
173         }
174
175         if (!currentTile && availableTile) {
176             XLOG("STEALING tile %d, %d (draw count %llu) for tile %d, %d",
177                   availableTile->x(), availableTile->y(), availableTile->drawCount(), x, y);
178             availableTile->discardTextures(); // don't wait for textures to be stolen
179             currentTile = availableTile;
180         }
181
182         if (!currentTile) {
183             XLOG("ERROR: No tile available for tile %d %d", x, y);
184         }
185
186         if (currentTile) {
187             currentTile->setGLWebViewState(m_glWebViewState);
188             currentTile->setPage(this);
189
190             currentTile->setContents(this, x, y, m_scale);
191
192             // TODO: move below (which is largely the same for layers / tiled
193             // page) into prepare() function
194
195             // ensure there is a texture associated with the tile and then check to
196             // see if the texture is dirty and in need of repainting
197             if (currentTile->isDirty() || !currentTile->frontTexture())
198                 currentTile->reserveTexture();
199             if (currentTile->backTexture()
200                     && currentTile->isDirty()
201                     && !currentTile->isRepaintPending()) {
202                 PaintTileOperation *operation = new PaintTileOperation(currentTile);
203                 TilesManager::instance()->scheduleOperation(operation);
204             }
205         }
206     }
207 }
208
209 bool TiledPage::updateTileDirtiness(const SkIRect& tileBounds)
210 {
211     if (!m_glWebViewState || tileBounds.isEmpty()) {
212         m_invalRegion.setEmpty();
213         m_invalTilesRegion.setEmpty();
214         return false;
215     }
216
217     bool visibleTileIsDirty = false;
218     for (int x = 0; x < m_baseTileSize; x++) {
219
220         BaseTile& tile = m_baseTiles[x];
221
222         // if the tile is in the dirty region then we must invalidate it
223         if (m_invalRegion.contains(tile.x(), tile.y())) {
224             tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
225             if (tileBounds.contains(tile.x(), tile.y()))
226                 visibleTileIsDirty = true;
227         }
228     }
229
230     // clear the invalidated region as all tiles within that region have now
231     // been marked as dirty.
232     m_invalRegion.setEmpty();
233     m_invalTilesRegion.setEmpty();
234     return visibleTileIsDirty;
235 }
236
237 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds, PrepareBounds bounds)
238 {
239     if (!m_glWebViewState)
240         return;
241
242     TilesManager::instance()->gatherTextures();
243     m_scrollingDown = goingDown;
244
245     int firstTileX = tileBounds.fLeft;
246     int firstTileY = tileBounds.fTop;
247     int nbTilesWidth = tileBounds.width();
248     int nbTilesHeight = tileBounds.height();
249
250     int lastTileX = tileBounds.fRight - 1;
251     int lastTileY = tileBounds.fBottom - 1;
252
253     // Expand number of tiles to allow tiles outside of viewport to be prepared for
254     // smoother scrolling.
255     int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
256     int nMaxTilesPerPage = m_baseTileSize / 2;
257
258     if (bounds == ExpandedBounds) {
259         // prepare tiles outside of the visible bounds
260         int expandX = m_glWebViewState->expandedTileBoundsX();
261         int expandY = m_glWebViewState->expandedTileBoundsY();
262
263         firstTileX -= expandX;
264         lastTileX += expandX;
265         nbTilesWidth += expandX * 2;
266
267         firstTileY -= expandY;
268         lastTileY += expandY;
269         nbTilesHeight += expandY * 2;
270     }
271
272     m_expandedTileBounds.fLeft = firstTileX;
273     m_expandedTileBounds.fTop = firstTileY;
274     m_expandedTileBounds.fRight = lastTileX;
275     m_expandedTileBounds.fBottom = lastTileY;
276
277     // check against corrupted scale values giving bad height/width (use float to avoid overflow)
278     float numTiles = static_cast<float>(nbTilesHeight) * static_cast<float>(nbTilesWidth);
279     if (numTiles > TilesManager::getMaxTextureAllocation() || nbTilesHeight < 1 || nbTilesWidth < 1)
280     {
281         XLOGC("ERROR: We don't have enough tiles for this page!"
282               " nbTilesHeight %d nbTilesWidth %d", nbTilesHeight, nbTilesWidth);
283         return;
284     }
285     for (int i = 0; i < nbTilesHeight; i++)
286         prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
287
288     m_prepare = true;
289 }
290
291 bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapMethod swap)
292 {
293     if (!m_glWebViewState)
294         return false;
295
296     if (!m_invalRegion.isEmpty() && !m_prepare)
297         return false;
298
299     if (m_scale != scale)
300         return false;
301
302     int swaps = 0;
303     bool fullSwap = true;
304     if (swap == SwapWholePage) {
305         for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
306             for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
307                 BaseTile* t = getBaseTile(x, y);
308                 if (!t || !t->isTileReady())
309                     return false;
310             }
311         }
312     } else { // SwapWhateveryIsReady
313         for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
314             for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
315                 BaseTile* t = getBaseTile(x, y);
316                 if (!t || !t->isTileReady())
317                     fullSwap = false;
318             }
319         }
320     }
321
322     // swap every tile on page (even if off screen)
323     for (int j = 0; j < m_baseTileSize; j++) {
324         BaseTile& tile = m_baseTiles[j];
325         if (tile.swapTexturesIfNeeded())
326             swaps++;
327     }
328
329     XLOG("%p %s swapped %d textures, returning true",
330          this, (swap == SwapWholePage) ? "whole page" : "greedy swap", swaps);
331     return fullSwap;
332 }
333
334
335 void TiledPage::draw(float transparency, const SkIRect& tileBounds)
336 {
337     if (!m_glWebViewState)
338         return;
339
340     const float tileWidth = TilesManager::tileWidth() * m_invScale;
341     const float tileHeight = TilesManager::tileHeight() * m_invScale;
342
343     SkIRect actualTileBounds = tileBounds;
344     actualTileBounds.fTop -= m_glWebViewState->expandedTileBoundsY();
345     actualTileBounds.fBottom += m_glWebViewState->expandedTileBoundsY();
346     actualTileBounds.fLeft -= m_glWebViewState->expandedTileBoundsX();
347     actualTileBounds.fRight += m_glWebViewState->expandedTileBoundsX();
348
349     actualTileBounds.fTop = std::max(0, actualTileBounds.fTop);
350     actualTileBounds.fLeft = std::max(0, actualTileBounds.fLeft);
351
352     for (int j = 0; j < m_baseTileSize; j++) {
353         BaseTile& tile = m_baseTiles[j];
354         bool tileInView = actualTileBounds.contains(tile.x(), tile.y());
355         if (tileInView) {
356             SkRect rect;
357             rect.fLeft = tile.x() * tileWidth;
358             rect.fTop = tile.y() * tileHeight;
359             rect.fRight = rect.fLeft + tileWidth;
360             rect.fBottom = rect.fTop + tileHeight;
361
362             tile.draw(transparency, rect, m_scale);
363         }
364
365         TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
366     }
367 }
368
369 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
370 {
371     // TODO: consider other flags so the pre-rendered tiles aren't so ugly
372     static SkPaintFlagsDrawFilter prefetchFilter(SkPaint::kAllFlags, 0);
373
374     if (!m_glWebViewState)
375         return false;
376
377     if (isPrefetchPage())
378         canvas->setDrawFilter(&prefetchFilter);
379
380     *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
381     return true;
382 }
383
384 void TiledPage::paintExtra(SkCanvas* canvas)
385 {
386 }
387
388 TiledPage* TiledPage::sibling()
389 {
390     if (!m_glWebViewState)
391         return 0;
392     return m_glWebViewState->sibling(this);
393 }
394
395 } // namespace WebCore
396
397 #endif // USE(ACCELERATED_COMPOSITING)