1 /* 2 * Copyright 2011, 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 #define LOG_TAG "TileGrid" 27 #define LOG_NDEBUG 1 28 29 #include "config.h" 30 #include "TileGrid.h" 31 32 #include "AndroidLog.h" 33 #include "DrawQuadData.h" 34 #include "GLWebViewState.h" 35 #include "PaintTileOperation.h" 36 #include "Tile.h" 37 #include "TileTexture.h" 38 #include "TilesManager.h" 39 40 #include <wtf/CurrentTime.h> 41 42 #define EXPANDED_BOUNDS_INFLATE 1 43 #define EXPANDED_PREFETCH_BOUNDS_Y_INFLATE 1 44 45 namespace WebCore { 46 47 TileGrid::TileGrid(bool isBaseSurface) 48 : m_prevTileY(0) 49 , m_scale(1) 50 , m_isBaseSurface(isBaseSurface) 51 { 52 m_dirtyRegion.setEmpty(); 53 #ifdef DEBUG_COUNT 54 ClassTracker::instance()->increment("TileGrid"); 55 #endif 56 } 57 58 TileGrid::~TileGrid() 59 { 60 #ifdef DEBUG_COUNT 61 ClassTracker::instance()->decrement("TileGrid"); 62 #endif 63 removeTiles(); 64 } 65 66 bool TileGrid::isReady() 67 { 68 bool tilesAllReady = true; 69 bool tilesVisible = false; 70 for (unsigned int i = 0; i < m_tiles.size(); i++) { 71 Tile* tile = m_tiles[i]; 72 if (tile->isTileVisible(m_area)) { 73 tilesVisible = true; 74 if (!tile->isTileReady()) { 75 tilesAllReady = false; 76 break; 77 } 78 } 79 } 80 // For now, if no textures are available, consider ourselves as ready 81 // in order to unblock the zooming process. 82 // FIXME: have a better system -- maybe keeping the last scale factor 83 // able to fully render everything 84 ALOGV("TG %p, ready %d, visible %d, texturesRemain %d", 85 this, tilesAllReady, tilesVisible, 86 TilesManager::instance()->layerTexturesRemain()); 87 88 return !TilesManager::instance()->layerTexturesRemain() 89 || !tilesVisible || tilesAllReady; 90 } 91 92 bool TileGrid::isMissingContent() 93 { 94 for (unsigned int i = 0; i < m_tiles.size(); i++) 95 if (m_tiles[i]->isTileVisible(m_area) && !m_tiles[i]->frontTexture()) 96 return true; 97 return false; 98 } 99 100 bool TileGrid::swapTiles() 101 { 102 int swaps = 0; 103 for (unsigned int i = 0; i < m_tiles.size(); i++) 104 if (m_tiles[i]->swapTexturesIfNeeded()) 105 swaps++; 106 ALOGV("TG %p swapping, swaps = %d", this, swaps); 107 return swaps != 0; 108 } 109 110 IntRect TileGrid::computeTilesArea(const IntRect& contentArea, float scale) 111 { 112 IntRect computedArea; 113 IntRect area(contentArea.x() * scale, 114 contentArea.y() * scale, 115 ceilf(contentArea.width() * scale), 116 ceilf(contentArea.height() * scale)); 117 118 ALOGV("TG prepare, scale %f, area %d x %d", scale, area.width(), area.height()); 119 120 if (area.width() == 0 && area.height() == 0) { 121 computedArea.setWidth(0); 122 computedArea.setHeight(0); 123 return computedArea; 124 } 125 126 int tileWidth = TilesManager::tileWidth(); 127 int tileHeight = TilesManager::tileHeight(); 128 129 computedArea.setX(area.x() / tileWidth); 130 computedArea.setY(area.y() / tileHeight); 131 float right = (area.x() + area.width()) / (float) tileWidth; 132 float bottom = (area.y() + area.height()) / (float) tileHeight; 133 computedArea.setWidth(ceilf(right) - computedArea.x()); 134 computedArea.setHeight(ceilf(bottom) - computedArea.y()); 135 return computedArea; 136 } 137 138 void TileGrid::prepareGL(GLWebViewState* state, float scale, 139 const IntRect& prepareArea, const IntRect& fullContentArea, 140 TilePainter* painter, int regionFlags, bool isLowResPrefetch, 141 bool updateWithBlit) 142 { 143 // first, how many tiles do we need 144 m_area = computeTilesArea(prepareArea, scale); 145 if (m_area.isEmpty()) 146 return; 147 148 ALOGV("prepare TileGrid %p with scale %.2f, prepareArea " 149 " %d, %d - %d x %d, corresponding to %d, %d x - %d x %d tiles", 150 this, scale, 151 prepareArea.x(), prepareArea.y(), 152 prepareArea.width(), prepareArea.height(), 153 m_area.x(), m_area.y(), 154 m_area.width(), m_area.height()); 155 156 bool goingDown = m_prevTileY < m_area.y(); 157 m_prevTileY = m_area.y(); 158 159 TilesManager* tilesManager = TilesManager::instance(); 160 if (scale != m_scale) 161 tilesManager->removeOperationsForFilter(new ScaleFilter(painter, m_scale)); 162 163 m_scale = scale; 164 165 // apply dirty region to affected tiles 166 if (!m_dirtyRegion.isEmpty()) { 167 for (unsigned int i = 0; i < m_tiles.size(); i++) 168 m_tiles[i]->markAsDirty(m_dirtyRegion); 169 170 // log inval region for the base surface 171 if (m_isBaseSurface && tilesManager->getProfiler()->enabled()) { 172 SkRegion::Iterator iterator(m_dirtyRegion); 173 while (!iterator.done()) { 174 SkIRect r = iterator.rect(); 175 tilesManager->getProfiler()->nextInval(r, scale); 176 iterator.next(); 177 } 178 } 179 m_dirtyRegion.setEmpty(); 180 } 181 182 if (regionFlags & StandardRegion) { 183 for (int i = 0; i < m_area.width(); i++) { 184 if (goingDown) { 185 for (int j = 0; j < m_area.height(); j++) 186 prepareTile(m_area.x() + i, m_area.y() + j, 187 painter, state, isLowResPrefetch, false, updateWithBlit); 188 } else { 189 for (int j = m_area.height() - 1; j >= 0; j--) 190 prepareTile(m_area.x() + i, m_area.y() + j, 191 painter, state, isLowResPrefetch, false, updateWithBlit); 192 } 193 } 194 } 195 196 if (regionFlags & ExpandedRegion) { 197 IntRect fullArea = computeTilesArea(fullContentArea, scale); 198 IntRect expandedArea = m_area; 199 200 // on systems reporting highEndGfx=true and useMinimalMemory not set, use expanded bounds 201 if (tilesManager->highEndGfx() && !tilesManager->useMinimalMemory()) 202 expandedArea.inflate(EXPANDED_BOUNDS_INFLATE); 203 204 if (isLowResPrefetch) 205 expandedArea.inflateY(EXPANDED_PREFETCH_BOUNDS_Y_INFLATE); 206 207 // clip painting area to content 208 expandedArea.intersect(fullArea); 209 210 for (int i = expandedArea.x(); i < expandedArea.maxX(); i++) 211 for (int j = expandedArea.y(); j < expandedArea.maxY(); j++) 212 if (!m_area.contains(i, j)) 213 prepareTile(i, j, painter, state, isLowResPrefetch, true, updateWithBlit); 214 } 215 } 216 217 void TileGrid::markAsDirty(const SkRegion& invalRegion) 218 { 219 ALOGV("TG %p markAsDirty, current region empty %d, new empty %d", 220 this, m_dirtyRegion.isEmpty(), invalRegion.isEmpty()); 221 m_dirtyRegion.op(invalRegion, SkRegion::kUnion_Op); 222 } 223 224 void TileGrid::prepareTile(int x, int y, TilePainter* painter, 225 GLWebViewState* state, bool isLowResPrefetch, 226 bool isExpandPrefetch, bool shouldTryUpdateWithBlit) 227 { 228 Tile* tile = getTile(x, y); 229 if (!tile) { 230 bool isLayerTile = !m_isBaseSurface; 231 tile = new Tile(isLayerTile); 232 m_tiles.append(tile); 233 } 234 235 ALOGV("preparing tile %p at %d, %d, painter is %p", tile, x, y, painter); 236 237 tile->setContents(x, y, m_scale, isExpandPrefetch); 238 239 if (shouldTryUpdateWithBlit && tryBlitFromContents(tile, painter)) 240 return; 241 242 if (tile->isDirty() || !tile->frontTexture()) 243 tile->reserveTexture(); 244 245 if (tile->backTexture() && tile->isDirty()) { 246 TilesManager* tilesManager = TilesManager::instance(); 247 248 // if a scheduled repaint is still outstanding, update it with the new painter 249 if (tile->isRepaintPending() && tilesManager->tryUpdateOperationWithPainter(tile, painter)) 250 return; 251 252 ALOGV("painting TG %p's tile %d %d for LG %p, scale %f", this, x, y, painter, m_scale); 253 PaintTileOperation *operation = new PaintTileOperation(tile, painter, 254 state, isLowResPrefetch); 255 tilesManager->scheduleOperation(operation); 256 } 257 } 258 259 bool TileGrid::tryBlitFromContents(Tile* tile, TilePainter* painter) 260 { 261 return tile->frontTexture() 262 && !tile->frontTexture()->isPureColor() 263 && tile->frontTexture()->m_ownTextureId 264 && !tile->isRepaintPending() 265 && painter->blitFromContents(tile); 266 } 267 268 Tile* TileGrid::getTile(int x, int y) 269 { 270 for (unsigned int i = 0; i <m_tiles.size(); i++) { 271 Tile* tile = m_tiles[i]; 272 if (tile->x() == x && tile->y() == y) 273 return tile; 274 } 275 return 0; 276 } 277 278 unsigned int TileGrid::getImageTextureId() 279 { 280 if (m_tiles.size() == 1) { 281 if (m_tiles[0]->frontTexture()) 282 return m_tiles[0]->frontTexture()->m_ownTextureId; 283 } 284 return 0; 285 } 286 287 int TileGrid::nbTextures(const IntRect& area, float scale) 288 { 289 IntRect tileBounds = computeTilesArea(area, scale); 290 int numberTextures = tileBounds.width() * tileBounds.height(); 291 292 // add the number of dirty tiles in the bounds, as they take up double 293 // textures for double buffering 294 for (unsigned int i = 0; i <m_tiles.size(); i++) { 295 Tile* tile = m_tiles[i]; 296 if (tile->isDirty() 297 && tile->x() >= tileBounds.x() && tile->x() <= tileBounds.maxX() 298 && tile->y() >= tileBounds.y() && tile->y() <= tileBounds.maxY()) 299 numberTextures++; 300 } 301 return numberTextures; 302 } 303 304 void TileGrid::drawGL(const IntRect& visibleContentArea, float opacity, 305 const TransformationMatrix* transform, 306 const Color* background) 307 { 308 m_area = computeTilesArea(visibleContentArea, m_scale); 309 if (m_area.width() == 0 || m_area.height() == 0) 310 return; 311 312 float invScale = 1.0 / m_scale; 313 const float tileWidth = TilesManager::tileWidth() * invScale; 314 const float tileHeight = TilesManager::tileHeight() * invScale; 315 316 int drawn = 0; 317 318 SkRegion missingRegion; 319 bool semiOpaqueBaseSurface = 320 background ? (background->hasAlpha() && background->alpha() > 0) : false; 321 if (semiOpaqueBaseSurface) { 322 SkIRect totalArea = SkIRect::MakeXYWH(m_area.x(), m_area.y(), 323 m_area.width(), m_area.height()); 324 missingRegion = SkRegion(totalArea); 325 } 326 327 bool usePointSampling = 328 TilesManager::instance()->shader()->usePointSampling(m_scale, transform); 329 330 float minTileX = visibleContentArea.x() / tileWidth; 331 float minTileY = visibleContentArea.y() / tileWidth; 332 float maxTileWidth = visibleContentArea.maxX() / tileWidth; 333 float maxTileHeight = visibleContentArea.maxY() / tileWidth; 334 ALOGV("minTileX, minTileY, maxTileWidth, maxTileHeight %f, %f, %f %f", 335 minTileX, minTileY, maxTileWidth, maxTileHeight); 336 for (unsigned int i = 0; i < m_tiles.size(); i++) { 337 Tile* tile = m_tiles[i]; 338 339 bool tileInView = tile->isTileVisible(m_area); 340 if (tileInView) { 341 SkRect rect; 342 rect.fLeft = tile->x() * tileWidth; 343 rect.fTop = tile->y() * tileHeight; 344 rect.fRight = rect.fLeft + tileWidth; 345 rect.fBottom = rect.fTop + tileHeight; 346 ALOGV("tile %p (layer tile: %d) %d,%d at scale %.2f vs %.2f [ready: %d] dirty: %d", 347 tile, tile->isLayerTile(), tile->x(), tile->y(), 348 tile->scale(), m_scale, tile->isTileReady(), tile->isDirty()); 349 350 bool forceBaseBlending = background ? background->hasAlpha() : false; 351 352 float left = std::max(minTileX - tile->x(), 0.0f); 353 float top = std::max(minTileY - tile->y(), 0.0f); 354 float right = std::min(maxTileWidth - tile->x(), 1.0f); 355 float bottom = std::min(maxTileHeight - tile->y(), 1.0f); 356 if (left > 1.0f || top > 1.0f || right < 0.0f || bottom < 0.0f) { 357 ALOGE("Unexpected portion:left, top, right, bottom %f %f %f %f", 358 left, top, right, bottom); 359 left = 0.0f; 360 top = 0.0f; 361 right = 1.0f; 362 bottom = 1.0f; 363 } 364 FloatRect fillPortion(left, top, right - left, bottom - top); 365 366 bool success = tile->drawGL(opacity, rect, m_scale, transform, 367 forceBaseBlending, usePointSampling, fillPortion); 368 if (semiOpaqueBaseSurface && success) { 369 // Cut the successful drawn tile area from the missing region. 370 missingRegion.op(SkIRect::MakeXYWH(tile->x(), tile->y(), 1, 1), 371 SkRegion::kDifference_Op); 372 } 373 if (tile->frontTexture()) 374 drawn++; 375 } 376 377 // log tile information for base, high res tiles 378 if (m_isBaseSurface && background) 379 TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView); 380 } 381 382 // Draw missing Regions with blend turned on 383 if (semiOpaqueBaseSurface) 384 drawMissingRegion(missingRegion, opacity, background); 385 386 ALOGV("TG %p drew %d tiles, scale %f", 387 this, drawn, m_scale); 388 } 389 390 void TileGrid::drawMissingRegion(const SkRegion& region, float opacity, 391 const Color* background) 392 { 393 SkRegion::Iterator iterator(region); 394 const float tileWidth = TilesManager::tileWidth() / m_scale; 395 const float tileHeight = TilesManager::tileHeight() / m_scale; 396 while (!iterator.done()) { 397 SkIRect r = iterator.rect(); 398 SkRect rect; 399 rect.fLeft = r.x() * tileWidth; 400 rect.fTop = r.y() * tileHeight; 401 rect.fRight = rect.fLeft + tileWidth * r.width(); 402 rect.fBottom = rect.fTop + tileHeight * r.height(); 403 ALOGV("draw tile x y, %d %d (%d %d) opacity %f", r.x(), r.y(), 404 r.width(), r.height(), opacity); 405 // Skia is using pre-multiplied color. 406 Color postAlpha = Color(background->red() * background->alpha() / 255, 407 background->green() * background->alpha() / 255, 408 background->blue() * background->alpha() / 255, 409 background->alpha() ); 410 411 PureColorQuadData backGroundData(postAlpha, BaseQuad, 0, &rect, opacity); 412 TilesManager::instance()->shader()->drawQuad(&backGroundData); 413 iterator.next(); 414 } 415 } 416 417 void TileGrid::removeTiles() 418 { 419 for (unsigned int i = 0; i < m_tiles.size(); i++) { 420 delete m_tiles[i]; 421 } 422 m_tiles.clear(); 423 } 424 425 void TileGrid::discardTextures() 426 { 427 ALOGV("TG %p discarding textures", this); 428 for (unsigned int i = 0; i < m_tiles.size(); i++) 429 m_tiles[i]->discardTextures(); 430 } 431 432 } // namespace WebCore 433