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"
34 #include "TilesManager.h"
38 #include <cutils/log.h>
39 #include <wtf/CurrentTime.h>
40 #include <wtf/text/CString.h>
43 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
54 using namespace android;
56 TiledPage::TiledPage(int id, GLWebViewState* state)
62 , m_glWebViewState(state)
63 , m_latestPictureInval(0)
66 m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
68 ClassTracker::instance()->increment("TiledPage");
72 void TiledPage::updateBaseTileSize()
74 // This value must be at least 1 greater than the max number of allowed
75 // textures. This is because prepare() asks for a tile before it reserves
76 // a texture for that tile. If all textures are currently in use by the
77 // page then there will be no available tile and having the extra tile
78 // ensures that this does not happen. After claiming the extra tile the call
79 // to reserveTexture() will cause some other tile in the page to lose it's
80 // texture and become available, thus ensuring that we always have at least
81 // one tile that is available.
82 int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
83 if (baseTileSize > m_baseTileSize)
84 m_baseTileSize = baseTileSize;
87 TiledPage::~TiledPage()
89 // In order to delete the page we must ensure that none of its BaseTiles are
90 // currently painting or scheduled to be painted by the TextureGenerator
91 TilesManager::instance()->removeOperationsForPage(this);
94 ClassTracker::instance()->decrement("TiledPage");
98 BaseTile* TiledPage::getBaseTile(int x, int y) const
100 for (int j = 0; j < m_baseTileSize; j++) {
101 BaseTile& tile = m_baseTiles[j];
102 if (tile.x() == x && tile.y() == y && !tile.isAvailable())
108 void TiledPage::setUsable(bool usable)
110 for (int j = 0; j < m_baseTileSize; j++) {
111 BaseTile& tile = m_baseTiles[j];
112 tile.setUsable(usable);
117 void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
119 // Given the current scale level we need to mark the appropriate tiles as dirty
120 const float invTileContentWidth = m_scale / TilesManager::tileWidth();
121 const float invTileContentHeight = m_scale / TilesManager::tileHeight();
123 const int firstDirtyTileX = static_cast<int>(floorf(inval.x() * invTileContentWidth));
124 const int firstDirtyTileY = static_cast<int>(floorf(inval.y() * invTileContentHeight));
125 const int lastDirtyTileX = static_cast<int>(ceilf(inval.maxX() * invTileContentWidth));
126 const int lastDirtyTileY = static_cast<int>(ceilf(inval.maxY() * invTileContentHeight));
128 XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
129 // We defer marking the tile as dirty until the next time we need to prepare
131 m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
132 m_invalTilesRegion.op(inval.x(), inval.y(), inval.maxX(), inval.maxY(), SkRegion::kUnion_Op);
133 m_latestPictureInval = pictureCount;
136 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
141 for (int i = 0; i < tilesInRow; i++) {
144 // If we are goingLeft, we want to schedule the tiles starting from the
145 // right (and to the left if not). This is because tiles are appended to
146 // the list and the texture uploader goes through the set front to back.
148 x += (tilesInRow - 1) - i;
155 BaseTile* currentTile = 0;
156 BaseTile* availableTile = 0;
157 for (int j = 0; j < m_baseTileSize; j++) {
158 BaseTile& tile = m_baseTiles[j];
159 if (tile.x() == x && tile.y() == y) {
163 if (!availableTile && tile.isAvailable())
164 availableTile = &tile;
167 if (!currentTile && availableTile) {
168 currentTile = availableTile;
172 currentTile->setGLWebViewState(m_glWebViewState);
173 currentTile->setContents(this, x, y, m_scale);
174 currentTile->setPage(this);
176 // ensure there is a texture associated with the tile and then check to
177 // see if the texture is dirty and in need of repainting
178 currentTile->reserveTexture();
179 updateTileUsedLevel(tileBounds, *currentTile);
180 if (currentTile->isDirty() && !currentTile->isRepaintPending()) {
181 PaintTileOperation *operation = new PaintTileOperation(currentTile);
182 TilesManager::instance()->scheduleOperation(operation);
183 } else if (currentTile->isDirty()) {
184 XLOG("Tile %dx%d is dirty, but awaiting repaint", currentTile->x(), currentTile->y());
190 void TiledPage::updateTileUsedLevel(const SkIRect& tileBounds, BaseTile& tile)
192 const int lastTileX = tileBounds.fRight - 1;
193 const int lastTileY = tileBounds.fBottom - 1;
195 // set the used level of the tile (e.g. distance from the viewport)
199 if (tileBounds.fLeft > tile.x())
200 dx = tileBounds.fLeft - tile.x();
201 else if (lastTileX < tile.x())
202 dx = tile.x() - lastTileX;
204 if (tileBounds.fTop > tile.y())
205 dy = tileBounds.fTop - tile.y();
206 else if (lastTileY < tile.y())
207 dy = tile.y() - lastTileY;
209 int d = std::max(dx, dy);
211 tile.setUsedLevel(d);
214 void TiledPage::updateTileState(const SkIRect& tileBounds)
216 if (!m_glWebViewState || tileBounds.isEmpty()) {
217 m_invalRegion.setEmpty();
218 m_invalTilesRegion.setEmpty();
222 for (int x = 0; x < m_baseTileSize; x++) {
224 BaseTile& tile = m_baseTiles[x];
226 // if the tile no longer has a texture then proceed to the next tile
227 if (tile.isAvailable())
230 // if the tile is in the dirty region then we must invalidate it
231 if (m_invalRegion.contains(tile.x(), tile.y()))
232 tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
234 updateTileUsedLevel(tileBounds, tile);
237 // clear the invalidated region as all tiles within that region have now
238 // been marked as dirty.
239 m_invalRegion.setEmpty();
240 m_invalTilesRegion.setEmpty();
243 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds)
245 if (!m_glWebViewState)
248 TilesManager::instance()->gatherTextures();
249 // update the tiles distance from the viewport
250 updateTileState(tileBounds);
252 m_scrollingDown = goingDown;
254 int firstTileX = tileBounds.fLeft;
255 int firstTileY = tileBounds.fTop;
256 int nbTilesWidth = tileBounds.width();
257 int nbTilesHeight = tileBounds.height();
259 int lastTileX = tileBounds.fRight - 1;
260 int lastTileY = tileBounds.fBottom - 1;
262 // Expand number of tiles to allow tiles outside of viewport to be prepared for
263 // smoother scrolling.
264 int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
265 int nMaxTilesPerPage = m_baseTileSize / 2;
266 int expandX = m_glWebViewState->expandedTileBoundsX();
267 int expandY = m_glWebViewState->expandedTileBoundsY();
269 firstTileX -= expandX;
270 lastTileX += expandX;
271 nbTilesWidth += expandX * 2;
273 firstTileY -= expandY;
274 lastTileY += expandY;
275 nbTilesHeight += expandY * 2;
277 m_expandedTileBounds.fLeft = firstTileX;
278 m_expandedTileBounds.fTop = firstTileY;
279 m_expandedTileBounds.fRight = lastTileX;
280 m_expandedTileBounds.fBottom = lastTileY;
282 for (int i = 0; i < nbTilesHeight; i++)
283 prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
286 bool TiledPage::ready(const SkIRect& tileBounds, float scale)
288 if (!m_glWebViewState)
291 if (!m_invalRegion.isEmpty() && !m_prepare)
294 if (m_scale != scale)
297 for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
298 for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
299 BaseTile* t = getBaseTile(x, y);
300 if (!t || !t->isTileReady())
308 void TiledPage::draw(float transparency, const SkIRect& tileBounds)
310 if (!m_glWebViewState)
313 const float tileWidth = TilesManager::tileWidth() * m_invScale;
314 const float tileHeight = TilesManager::tileHeight() * m_invScale;
316 SkIRect actualTileBounds = tileBounds;
317 actualTileBounds.fTop -= m_glWebViewState->expandedTileBoundsY();
318 actualTileBounds.fBottom += m_glWebViewState->expandedTileBoundsY();
319 actualTileBounds.fLeft -= m_glWebViewState->expandedTileBoundsX();
320 actualTileBounds.fRight += m_glWebViewState->expandedTileBoundsX();
322 for (int j = 0; j < m_baseTileSize; j++) {
323 BaseTile& tile = m_baseTiles[j];
324 bool tileInView = actualTileBounds.contains(tile.x(), tile.y());
328 rect.fLeft = tile.x() * tileWidth;
329 rect.fTop = tile.y() * tileHeight;
330 rect.fRight = rect.fLeft + tileWidth;
331 rect.fBottom = rect.fTop + tileHeight;
333 tile.draw(transparency, rect, m_scale);
336 TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
340 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
342 if (!m_glWebViewState)
345 *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
349 void TiledPage::paintExtra(SkCanvas* canvas)
353 TiledPage* TiledPage::sibling()
355 if (!m_glWebViewState)
357 return m_glWebViewState->sibling(this);
360 } // namespace WebCore
362 #endif // USE(ACCELERATED_COMPOSITING)