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"
36 #include <cutils/log.h>
37 #include <wtf/CurrentTime.h>
38 #include <wtf/text/CString.h>
41 #define XLOGC(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
46 #define XLOG(...) android_printLog(ANDROID_LOG_DEBUG, "TiledPage", __VA_ARGS__)
57 using namespace android;
59 TiledPage::TiledPage(int id, GLWebViewState* state)
65 , m_glWebViewState(state)
66 , m_latestPictureInval(0)
69 m_baseTiles = new BaseTile[TilesManager::getMaxTextureAllocation() + 1];
71 ClassTracker::instance()->increment("TiledPage");
75 void TiledPage::updateBaseTileSize()
77 // This value must be at least 1 greater than the max number of allowed
78 // textures. This is because prepare() asks for a tile before it reserves
79 // a texture for that tile. If all textures are currently in use by the
80 // page then there will be no available tile and having the extra tile
81 // ensures that this does not happen. After claiming the extra tile the call
82 // to reserveTexture() will cause some other tile in the page to lose it's
83 // texture and become available, thus ensuring that we always have at least
84 // one tile that is available.
85 int baseTileSize = TilesManager::instance()->maxTextureCount() + 1;
86 if (baseTileSize > m_baseTileSize)
87 m_baseTileSize = baseTileSize;
90 TiledPage::~TiledPage()
92 // In order to delete the page we must ensure that none of its BaseTiles are
93 // currently painting or scheduled to be painted by the TextureGenerator
94 TilesManager::instance()->removeOperationsForPage(this);
97 ClassTracker::instance()->decrement("TiledPage");
101 BaseTile* TiledPage::getBaseTile(int x, int y) const
103 // TODO: replace loop over array with HashMap indexing
104 for (int j = 0; j < m_baseTileSize; j++) {
105 BaseTile& tile = m_baseTiles[j];
106 if (tile.x() == x && tile.y() == y)
112 void TiledPage::discardTextures()
114 for (int j = 0; j < m_baseTileSize; j++) {
115 BaseTile& tile = m_baseTiles[j];
116 tile.discardTextures();
121 void TiledPage::invalidateRect(const IntRect& inval, const unsigned int pictureCount)
123 // Given the current scale level we need to mark the appropriate tiles as dirty
124 const float invTileContentWidth = m_scale / TilesManager::tileWidth();
125 const float invTileContentHeight = m_scale / TilesManager::tileHeight();
127 const int firstDirtyTileX = static_cast<int>(floorf(inval.x() * invTileContentWidth));
128 const int firstDirtyTileY = static_cast<int>(floorf(inval.y() * invTileContentHeight));
129 const int lastDirtyTileX = static_cast<int>(ceilf(inval.maxX() * invTileContentWidth));
130 const int lastDirtyTileY = static_cast<int>(ceilf(inval.maxY() * invTileContentHeight));
132 XLOG("Marking X %d-%d and Y %d-%d dirty", firstDirtyTileX, lastDirtyTileX, firstDirtyTileY, lastDirtyTileY);
133 // We defer marking the tile as dirty until the next time we need to prepare
135 m_invalRegion.op(firstDirtyTileX, firstDirtyTileY, lastDirtyTileX, lastDirtyTileY, SkRegion::kUnion_Op);
136 m_invalTilesRegion.op(inval.x(), inval.y(), inval.maxX(), inval.maxY(), SkRegion::kUnion_Op);
137 m_latestPictureInval = pictureCount;
140 void TiledPage::prepareRow(bool goingLeft, int tilesInRow, int firstTileX, int y, const SkIRect& tileBounds)
145 for (int i = 0; i < tilesInRow; i++) {
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.
152 x += (tilesInRow - 1) - i;
159 BaseTile* currentTile = 0;
160 BaseTile* availableTile = 0;
161 for (int j = 0; j < m_baseTileSize; j++) {
162 BaseTile& tile = m_baseTiles[j];
163 if (tile.x() == x && tile.y() == y) {
168 if (!availableTile || (tile.drawCount() < availableTile->drawCount()))
169 availableTile = &tile;
172 if (!currentTile && availableTile) {
173 XLOG("STEALING tile %d, %d (draw count %llu) for tile %d, %d",
174 availableTile->x(), availableTile->y(), availableTile->drawCount(), x, y);
175 currentTile = availableTile;
179 XLOG("ERROR: No tile available for tile %d %d", x, y);
183 currentTile->setGLWebViewState(m_glWebViewState);
184 currentTile->setPage(this);
186 currentTile->setContents(this, x, y, m_scale);
188 // TODO: move below (which is largely the same for layers / tiled
189 // page) into prepare() function
191 // ensure there is a texture associated with the tile and then check to
192 // see if the texture is dirty and in need of repainting
193 if (currentTile->isDirty() || !currentTile->frontTexture())
194 currentTile->reserveTexture();
195 if (currentTile->backTexture()
196 && currentTile->isDirty()
197 && !currentTile->isRepaintPending()) {
198 PaintTileOperation *operation = new PaintTileOperation(currentTile);
199 TilesManager::instance()->scheduleOperation(operation);
205 bool TiledPage::updateTileDirtiness(const SkIRect& tileBounds)
207 if (!m_glWebViewState || tileBounds.isEmpty()) {
208 m_invalRegion.setEmpty();
209 m_invalTilesRegion.setEmpty();
213 bool visibleTileIsDirty = false;
214 for (int x = 0; x < m_baseTileSize; x++) {
216 BaseTile& tile = m_baseTiles[x];
218 // if the tile is in the dirty region then we must invalidate it
219 if (m_invalRegion.contains(tile.x(), tile.y())) {
220 tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
221 if (tileBounds.contains(tile.x(), tile.y()))
222 visibleTileIsDirty = true;
226 // clear the invalidated region as all tiles within that region have now
227 // been marked as dirty.
228 m_invalRegion.setEmpty();
229 m_invalTilesRegion.setEmpty();
230 return visibleTileIsDirty;
233 void TiledPage::prepare(bool goingDown, bool goingLeft, const SkIRect& tileBounds, PrepareBounds bounds)
235 if (!m_glWebViewState)
238 TilesManager::instance()->gatherTextures();
239 m_scrollingDown = goingDown;
241 int firstTileX = tileBounds.fLeft;
242 int firstTileY = tileBounds.fTop;
243 int nbTilesWidth = tileBounds.width();
244 int nbTilesHeight = tileBounds.height();
246 int lastTileX = tileBounds.fRight - 1;
247 int lastTileY = tileBounds.fBottom - 1;
249 // Expand number of tiles to allow tiles outside of viewport to be prepared for
250 // smoother scrolling.
251 int nTilesToPrepare = nbTilesWidth * nbTilesHeight;
252 int nMaxTilesPerPage = m_baseTileSize / 2;
254 if (bounds == ExpandedBounds) {
255 // prepare tiles outside of the visible bounds
256 int expandX = m_glWebViewState->expandedTileBoundsX();
257 int expandY = m_glWebViewState->expandedTileBoundsY();
259 firstTileX -= expandX;
260 lastTileX += expandX;
261 nbTilesWidth += expandX * 2;
263 firstTileY -= expandY;
264 lastTileY += expandY;
265 nbTilesHeight += expandY * 2;
268 m_expandedTileBounds.fLeft = firstTileX;
269 m_expandedTileBounds.fTop = firstTileY;
270 m_expandedTileBounds.fRight = lastTileX;
271 m_expandedTileBounds.fBottom = lastTileY;
273 if (nbTilesHeight * nbTilesWidth > TilesManager::getMaxTextureAllocation() + 1) {
274 XLOGC("ERROR: We don't have enough tiles for this page!"
275 " nbTilesHeight %d nbTilesWidth %d", nbTilesHeight, nbTilesWidth);
278 for (int i = 0; i < nbTilesHeight; i++)
279 prepareRow(goingLeft, nbTilesWidth, firstTileX, firstTileY + i, tileBounds);
284 bool TiledPage::swapBuffersIfReady(const SkIRect& tileBounds, float scale, SwapMethod swap)
286 if (!m_glWebViewState)
289 if (!m_invalRegion.isEmpty() && !m_prepare)
292 if (m_scale != scale)
296 if (swap == SwapWholePage) {
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())
304 for (int x = tileBounds.fLeft; x < tileBounds.fRight; x++) {
305 for (int y = tileBounds.fTop; y < tileBounds.fBottom; y++) {
306 BaseTile* t = getBaseTile(x, y);
307 if (t->swapTexturesIfNeeded())
311 XLOG("%p whole page swapped %d textures, returning true", this, swaps);
313 } else { // SwapWhateveryIsReady
314 bool fullSwap = true;
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())
321 if (t->swapTexturesIfNeeded())
326 XLOG("%p greedy swap swapped %d tiles, returning %d", this, swaps, fullSwap);
332 void TiledPage::draw(float transparency, const SkIRect& tileBounds)
334 if (!m_glWebViewState)
337 const float tileWidth = TilesManager::tileWidth() * m_invScale;
338 const float tileHeight = TilesManager::tileHeight() * m_invScale;
340 SkIRect actualTileBounds = tileBounds;
341 actualTileBounds.fTop -= m_glWebViewState->expandedTileBoundsY();
342 actualTileBounds.fBottom += m_glWebViewState->expandedTileBoundsY();
343 actualTileBounds.fLeft -= m_glWebViewState->expandedTileBoundsX();
344 actualTileBounds.fRight += m_glWebViewState->expandedTileBoundsX();
346 actualTileBounds.fTop = std::max(0, actualTileBounds.fTop);
347 actualTileBounds.fLeft = std::max(0, actualTileBounds.fLeft);
349 for (int j = 0; j < m_baseTileSize; j++) {
350 BaseTile& tile = m_baseTiles[j];
351 bool tileInView = actualTileBounds.contains(tile.x(), tile.y());
354 rect.fLeft = tile.x() * tileWidth;
355 rect.fTop = tile.y() * tileHeight;
356 rect.fRight = rect.fLeft + tileWidth;
357 rect.fBottom = rect.fTop + tileHeight;
359 tile.draw(transparency, rect, m_scale);
362 TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
366 bool TiledPage::paint(BaseTile* tile, SkCanvas* canvas, unsigned int* pictureUsed)
368 if (!m_glWebViewState)
371 *pictureUsed = m_glWebViewState->paintBaseLayerContent(canvas);
375 void TiledPage::paintExtra(SkCanvas* canvas)
379 TiledPage* TiledPage::sibling()
381 if (!m_glWebViewState)
383 return m_glWebViewState->sibling(this);
386 } // namespace WebCore
388 #endif // USE(ACCELERATED_COMPOSITING)