2 * Copyright 2010, The Android Open Source Project
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "TiledPage.h"
29 #if USE(ACCELERATED_COMPOSITING)
33 #include "PaintTileOperation.h"
35 #include "SkPaintFlagsDrawFilter.h"
36 #include "TilesManager.h"
38 #include <cutils/log.h>
39 #include <wtf/CurrentTime.h>
40 #include <wtf/text/CString.h>
43 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
48 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
59 using namespace android;
61 TiledPage::TiledPage(int id, GLWebViewState* state)
67 , m_glWebViewState(state)
68 , m_latestPictureInval(0)
70 , m_isPrefetchPage(false)
73 m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
75 ClassTracker::instance()->increment("TiledPage");
79 void TiledPage::updateBaseTileSize()
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;
94 TiledPage::~TiledPage()
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);
101 ClassTracker::instance()->decrement("TiledPage");
105 BaseTile* TiledPage::getBaseTile(int x, int y) const
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)
116 void TiledPage::discardTextures()
118 for (int j = 0; j < m_baseTileSize; j++) {
119 BaseTile& tile = m_baseTiles[j];
120 tile.discardTextures();
125 void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
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();
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));
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
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;
144 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
146 for (int i = 0; i < tilesInRow; i++) {
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.
153 x += (tilesInRow - 1) - i;
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) {
166 if (!availableTile || (tile.drawCount() < availableTile->drawCount()))
167 availableTile = &tile;
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;
178 XLOG("ERROR: No tile available for tile %d %d", x, y);
182 currentTile->setGLWebViewState(m_glWebViewState);
183 currentTile->setPage(this);
185 currentTile->setContents(this, x, y, m_scale);
187 // TODO: move below (which is largely the same for layers / tiled
188 // page) into prepare() function
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);
204 bool TiledPage::updateTileDirtiness(const SkIRect& tileBounds)
206 if (!m_glWebViewState || tileBounds.isEmpty()) {
207 m_invalRegion.setEmpty();
208 m_invalTilesRegion.setEmpty();
212 bool visibleTileIsDirty = false;
213 for (int x = 0; x < m_baseTileSize; x++) {
215 BaseTile& tile = m_baseTiles[x];
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;
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;
232 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds, PrepareBounds bounds)
234 if (!m_glWebViewState)
237 TilesManager::instance()->gatherTextures();
238 m_scrollingDown = goingDown;
240 int firstTileX = tileBounds.fLeft;
241 int firstTileY = tileBounds.fTop;
242 int nbTilesWidth = tileBounds.width();
243 int nbTilesHeight = tileBounds.height();
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;
250 if (bounds == ExpandedBounds) {
251 // prepare tiles outside of the visible bounds
252 int expandX = m_glWebViewState->expandedTileBoundsX();
253 int expandY = m_glWebViewState->expandedTileBoundsY();
255 firstTileX -= expandX;
256 nbTilesWidth += expandX * 2;
258 firstTileY -= expandY;
259 nbTilesHeight += expandY * 2;
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();
266 // adjust perimeter to not go outside base content bounds
267 if (firstTileX < 0) {
268 nbTilesWidth += firstTileX;
271 if (firstTileY < 0) {
272 nbTilesHeight += firstTileY;
275 nbTilesWidth = std::min(nbTilesWidth, static_cast<int>(ceilf(maxWidthTiles)) - firstTileX);
276 nbTilesHeight = std::min(nbTilesHeight, static_cast<int>(ceilf(maxHeightTiles)) - firstTileY);
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)
282 XLOGC("ERROR: We don't have enough tiles for this page!"
283 " nbTilesHeight %d nbTilesWidth %d", nbTilesHeight, nbTilesWidth);
286 for (int i = 0; i < nbTilesHeight; i++)
287 prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
292 bool TiledPage::hasMissingContent(const SkIRect& tileBounds)
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())
302 return neededTiles > 0;
305 bool TiledPage::isReady(const SkIRect& tileBounds)
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())
316 XLOG("tiled page %p still needs %d ready tiles", this, neededTiles);
317 return neededTiles == 0;
320 bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale)
322 if (!m_glWebViewState)
325 if (!m_invalRegion.isEmpty() && !m_prepare)
328 if (m_scale != scale)
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())
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())
348 XLOG("%p greedy swapped %d textures, returning true", this, swaps);
352 void TiledPage::prepareForDrawGL(float transparency, const SkIRect& tileBounds)
355 m_transparency = transparency;
356 m_tileBounds = tileBounds;
359 void TiledPage::drawGL()
361 if (!m_glWebViewState || m_transparency == 0 || !m_willDraw)
364 const float tileWidth = TilesManager::tileWidth() * m_invScale;
365 const float tileHeight = TilesManager::tileHeight() * m_invScale;
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());
372 rect.fLeft = tile.x() * tileWidth;
373 rect.fTop = tile.y() * tileHeight;
374 rect.fRight = rect.fLeft + tileWidth;
375 rect.fBottom = rect.fTop + tileHeight;
377 tile.draw(m_transparency, rect, m_scale);
380 TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
382 m_willDraw = false; // don't redraw until re-prepared
385 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
387 static SkPaintFlagsDrawFilter prefetchFilter(SkPaint::kAllFlags,
388 SkPaint::kAntiAlias_Flag);
390 if (!m_glWebViewState)
393 if (isPrefetchPage())
394 canvas->setDrawFilter(&prefetchFilter);
396 *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
400 TiledPage* TiledPage::sibling()
402 if (!m_glWebViewState)
404 return m_glWebViewState->sibling(this);
407 } // namespace WebCore
409 #endif // USE(ACCELERATED_COMPOSITING)