1/* 2 * Copyright (C) 2011-2014 Apple Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "TileGrid.h" 28 29#include "GraphicsContext.h" 30#include "LayerPool.h" 31#include "PlatformCALayer.h" 32#include "TileController.h" 33#include <wtf/MainThread.h> 34 35#if PLATFORM(IOS) 36#include "TileControllerMemoryHandlerIOS.h" 37#endif 38 39namespace WebCore { 40 41TileGrid::TileGrid(TileController& controller) 42 : m_controller(controller) 43 , m_containerLayer(*controller.rootLayer().createCompatibleLayer(PlatformCALayer::LayerTypeLayer, nullptr)) 44 , m_scale(1) 45 , m_cohortRemovalTimer(this, &TileGrid::cohortRemovalTimerFired) 46{ 47#ifndef NDEBUG 48 m_containerLayer.get().setName("TileGrid Container Layer"); 49#endif 50} 51 52TileGrid::~TileGrid() 53{ 54 ASSERT(isMainThread()); 55 56 for (auto& tile : m_tiles.values()) 57 tile.layer->setOwner(nullptr); 58} 59 60void TileGrid::setScale(float scale) 61{ 62 m_scale = scale; 63 64 TransformationMatrix transform; 65 transform.scale(1 / m_scale); 66 m_containerLayer->setTransform(transform); 67 68 // FIXME: we may revalidateTiles twice in this commit. 69 revalidateTiles(PruneSecondaryTiles); 70 71 for (auto& tile : m_tiles.values()) 72 tile.layer->setContentsScale(m_controller.deviceScaleFactor()); 73} 74 75void TileGrid::setNeedsDisplay() 76{ 77 for (auto& entry : m_tiles) { 78 TileInfo& tileInfo = entry.value; 79 IntRect tileRect = rectForTileIndex(entry.key); 80 81 if (tileRect.intersects(m_primaryTileCoverageRect) && tileInfo.layer->superlayer()) 82 tileInfo.layer->setNeedsDisplay(); 83 else 84 tileInfo.hasStaleContent = true; 85 } 86} 87 88void TileGrid::setNeedsDisplayInRect(const IntRect& rect) 89{ 90 if (m_tiles.isEmpty()) 91 return; 92 93 FloatRect scaledRect(rect); 94 scaledRect.scale(m_scale); 95 IntRect repaintRectInTileCoords(enclosingIntRect(scaledRect)); 96 97 IntSize tileSize = m_controller.tileSize(); 98 99 // For small invalidations, lookup the covered tiles. 100 if (repaintRectInTileCoords.height() < 2 * tileSize.height() && repaintRectInTileCoords.width() < 2 * tileSize.width()) { 101 TileIndex topLeft; 102 TileIndex bottomRight; 103 getTileIndexRangeForRect(repaintRectInTileCoords, topLeft, bottomRight); 104 105 for (int y = topLeft.y(); y <= bottomRight.y(); ++y) { 106 for (int x = topLeft.x(); x <= bottomRight.x(); ++x) { 107 TileIndex tileIndex(x, y); 108 109 TileMap::iterator it = m_tiles.find(tileIndex); 110 if (it != m_tiles.end()) 111 setTileNeedsDisplayInRect(tileIndex, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect); 112 } 113 } 114 return; 115 } 116 117 for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) 118 setTileNeedsDisplayInRect(it->key, it->value, repaintRectInTileCoords, m_primaryTileCoverageRect); 119} 120 121void TileGrid::dropTilesInRect(const IntRect& rect) 122{ 123 if (m_tiles.isEmpty()) 124 return; 125 126 FloatRect scaledRect(rect); 127 scaledRect.scale(m_scale); 128 IntRect dropRectInTileCoords(enclosingIntRect(scaledRect)); 129 130 Vector<TileIndex> tilesToRemove; 131 132 for (auto& index : m_tiles.keys()) { 133 if (rectForTileIndex(index).intersects(dropRectInTileCoords)) 134 tilesToRemove.append(index); 135 } 136 137 removeTiles(tilesToRemove); 138} 139 140void TileGrid::setTileNeedsDisplayInRect(const TileIndex& tileIndex, TileInfo& tileInfo, const IntRect& repaintRectInTileCoords, const IntRect& coverageRectInTileCoords) 141{ 142 PlatformCALayer* tileLayer = tileInfo.layer.get(); 143 144 IntRect tileRect = rectForTileIndex(tileIndex); 145 FloatRect tileRepaintRect = tileRect; 146 tileRepaintRect.intersect(repaintRectInTileCoords); 147 if (tileRepaintRect.isEmpty()) 148 return; 149 150 tileRepaintRect.moveBy(-tileRect.location()); 151 152 // We could test for intersection with the visible rect. This would reduce painting yet more, 153 // but may make scrolling stale tiles into view more frequent. 154 if (tileRect.intersects(coverageRectInTileCoords) && tileLayer->superlayer()) { 155 tileLayer->setNeedsDisplay(&tileRepaintRect); 156 157 if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0)) { 158 FloatRect indicatorRect(0, 0, 52, 27); 159 tileLayer->setNeedsDisplay(&indicatorRect); 160 } 161 } else 162 tileInfo.hasStaleContent = true; 163} 164 165void TileGrid::updateTileLayerProperties() 166{ 167 bool acceleratesDrawing = m_controller.acceleratesDrawing(); 168 bool opaque = m_controller.tilesAreOpaque(); 169 Color tileDebugBorderColor = m_controller.tileDebugBorderColor(); 170 float tileDebugBorderWidth = m_controller.tileDebugBorderWidth(); 171 172 for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { 173 const TileInfo& tileInfo = it->value; 174 tileInfo.layer->setAcceleratesDrawing(acceleratesDrawing); 175 tileInfo.layer->setOpaque(opaque); 176 tileInfo.layer->setBorderColor(tileDebugBorderColor); 177 tileInfo.layer->setBorderWidth(tileDebugBorderWidth); 178 } 179} 180 181bool TileGrid::tilesWouldChangeForVisibleRect(const FloatRect& newVisibleRect, const FloatRect& oldVisibleRect) const 182{ 183 FloatRect visibleRect = newVisibleRect; 184 185 if (visibleRect.isEmpty()) 186 return false; 187 188 FloatRect currentTileCoverageRect = m_controller.computeTileCoverageRect(oldVisibleRect, newVisibleRect); 189 FloatRect scaledRect(currentTileCoverageRect); 190 scaledRect.scale(m_scale); 191 IntRect currentCoverageRectInTileCoords(enclosingIntRect(scaledRect)); 192 193 TileIndex topLeft; 194 TileIndex bottomRight; 195 getTileIndexRangeForRect(currentCoverageRectInTileCoords, topLeft, bottomRight); 196 197 IntRect coverageRect = rectForTileIndex(topLeft); 198 coverageRect.unite(rectForTileIndex(bottomRight)); 199 return coverageRect != m_primaryTileCoverageRect; 200} 201 202bool TileGrid::prepopulateRect(const FloatRect& rect) 203{ 204 IntRect enclosingCoverageRect = enclosingIntRect(rect); 205 if (m_primaryTileCoverageRect.contains(enclosingCoverageRect)) 206 return false; 207 208 m_secondaryTileCoverageRects.append(enclosingCoverageRect); 209 return true; 210} 211 212IntRect TileGrid::rectForTileIndex(const TileIndex& tileIndex) const 213{ 214 // FIXME: calculating the scaled size here should match with the rest of calculated sizes where we use the combination of 215 // enclosingIntRect, expandedIntSize (floor vs ceil). 216 // However enclosing this size could reveal gap on root layer's background. see RenderView::backgroundRect() 217 IntSize tileSize = m_controller.tileSize(); 218 IntRect rect(tileIndex.x() * tileSize.width(), tileIndex.y() * tileSize.height(), tileSize.width(), tileSize.height()); 219 IntRect scaledBounds(m_controller.bounds()); 220 scaledBounds.scale(m_scale); 221 rect.intersect(scaledBounds); 222 return rect; 223} 224 225void TileGrid::getTileIndexRangeForRect(const IntRect& rect, TileIndex& topLeft, TileIndex& bottomRight) const 226{ 227 IntRect clampedRect = m_controller.bounds(); 228 clampedRect.scale(m_scale); 229 clampedRect.intersect(rect); 230 231 auto tileSize = m_controller.tileSize(); 232 if (clampedRect.x() >= 0) 233 topLeft.setX(clampedRect.x() / tileSize.width()); 234 else 235 topLeft.setX(floorf((float)clampedRect.x() / tileSize.width())); 236 237 if (clampedRect.y() >= 0) 238 topLeft.setY(clampedRect.y() / tileSize.height()); 239 else 240 topLeft.setY(floorf((float)clampedRect.y() / tileSize.height())); 241 242 int bottomXRatio = ceil((float)clampedRect.maxX() / tileSize.width()); 243 bottomRight.setX(std::max(bottomXRatio - 1, 0)); 244 245 int bottomYRatio = ceil((float)clampedRect.maxY() / tileSize.height()); 246 bottomRight.setY(std::max(bottomYRatio - 1, 0)); 247} 248 249unsigned TileGrid::blankPixelCount() const 250{ 251 PlatformLayerList tiles(m_tiles.size()); 252 253 for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { 254 if (PlatformLayer *layer = it->value.layer->platformLayer()) 255 tiles.append(layer); 256 } 257 258 return TileController::blankPixelCountForTiles(tiles, m_controller.visibleRect(), IntPoint(0, 0)); 259} 260 261void TileGrid::removeTiles(Vector<TileGrid::TileIndex>& toRemove) 262{ 263 for (size_t i = 0; i < toRemove.size(); ++i) { 264 TileInfo tileInfo = m_tiles.take(toRemove[i]); 265 tileInfo.layer->removeFromSuperlayer(); 266 m_tileRepaintCounts.remove(tileInfo.layer.get()); 267 tileInfo.layer->moveToLayerPool(); 268 } 269} 270 271void TileGrid::removeAllSecondaryTiles() 272{ 273 Vector<TileIndex> tilesToRemove; 274 275 for (auto& entry : m_tiles) { 276 const TileInfo& tileInfo = entry.value; 277 if (tileInfo.cohort == VisibleTileCohort) 278 continue; 279 tilesToRemove.append(entry.key); 280 } 281 282 removeTiles(tilesToRemove); 283} 284 285void TileGrid::removeTilesInCohort(TileCohort cohort) 286{ 287 ASSERT(cohort != VisibleTileCohort); 288 Vector<TileIndex> tilesToRemove; 289 290 for (auto& entry : m_tiles) { 291 const TileInfo& tileInfo = entry.value; 292 if (tileInfo.cohort != cohort) 293 continue; 294 tilesToRemove.append(entry.key); 295 } 296 297 removeTiles(tilesToRemove); 298} 299 300void TileGrid::revalidateTiles(unsigned validationPolicy) 301{ 302 FloatRect visibleRect = m_controller.visibleRect(); 303 IntRect bounds = m_controller.bounds(); 304 305 if (visibleRect.isEmpty() || bounds.isEmpty()) 306 return; 307 308 FloatRect tileCoverageRect = m_controller.computeTileCoverageRect(m_controller.visibleRectAtLastRevalidate(), visibleRect); 309 FloatRect scaledRect(tileCoverageRect); 310 scaledRect.scale(m_scale); 311 IntRect coverageRectInTileCoords(enclosingIntRect(scaledRect)); 312 313 TileCohort currCohort = nextTileCohort(); 314 unsigned tilesInCohort = 0; 315 316 double minimumRevalidationTimerDuration = std::numeric_limits<double>::max(); 317 bool needsTileRevalidation = false; 318 319 // Move tiles newly outside the coverage rect into the cohort map. 320 for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { 321 TileInfo& tileInfo = it->value; 322 TileIndex tileIndex = it->key; 323 324 PlatformCALayer* tileLayer = tileInfo.layer.get(); 325 IntRect tileRect = rectForTileIndex(tileIndex); 326 if (tileRect.intersects(coverageRectInTileCoords)) { 327 tileInfo.cohort = VisibleTileCohort; 328 if (tileInfo.hasStaleContent) { 329 // FIXME: store a dirty region per layer? 330 tileLayer->setNeedsDisplay(); 331 tileInfo.hasStaleContent = false; 332 } 333 } else { 334 // Add to the currentCohort if not already in one. 335 if (tileInfo.cohort == VisibleTileCohort) { 336 tileInfo.cohort = currCohort; 337 ++tilesInCohort; 338 339 if (m_controller.unparentsOffscreenTiles()) 340 tileLayer->removeFromSuperlayer(); 341 } else if (m_controller.unparentsOffscreenTiles() && m_controller.shouldAggressivelyRetainTiles() && tileLayer->superlayer()) { 342 // Aggressive tile retention means we'll never remove cohorts, but we need to make sure they're unparented. 343 // We can't immediately unparent cohorts comprised of secondary tiles that never touch the primary coverage rect, 344 // because that would defeat the usefulness of prepopulateRect(); instead, age prepopulated tiles out as if they were being removed. 345 for (auto& cohort : m_cohortList) { 346 if (cohort.cohort != tileInfo.cohort) 347 continue; 348 double timeUntilCohortExpires = cohort.timeUntilExpiration(); 349 if (timeUntilCohortExpires > 0) { 350 minimumRevalidationTimerDuration = std::min(minimumRevalidationTimerDuration, timeUntilCohortExpires); 351 needsTileRevalidation = true; 352 } else 353 tileLayer->removeFromSuperlayer(); 354 break; 355 } 356 } 357 } 358 } 359 360 if (needsTileRevalidation) 361 m_controller.scheduleTileRevalidation(minimumRevalidationTimerDuration); 362 363 if (tilesInCohort) 364 startedNewCohort(currCohort); 365 366 if (!m_controller.shouldAggressivelyRetainTiles()) { 367 if (m_controller.shouldTemporarilyRetainTileCohorts()) 368 scheduleCohortRemoval(); 369 else if (tilesInCohort) 370 removeTilesInCohort(currCohort); 371 } 372 373 // Ensure primary tile coverage tiles. 374 m_primaryTileCoverageRect = ensureTilesForRect(tileCoverageRect, CoverageType::PrimaryTiles); 375 376 if (validationPolicy & PruneSecondaryTiles) { 377 removeAllSecondaryTiles(); 378 m_cohortList.clear(); 379 } else { 380 for (auto& secondaryCoverageRect : m_secondaryTileCoverageRects) { 381 FloatRect secondaryRectInLayerCoordinates(secondaryCoverageRect); 382 secondaryRectInLayerCoordinates.scale(1 / m_scale); 383 ensureTilesForRect(secondaryRectInLayerCoordinates, CoverageType::SecondaryTiles); 384 } 385 m_secondaryTileCoverageRects.clear(); 386 } 387 388 if (m_controller.unparentsOffscreenTiles() && (validationPolicy & UnparentAllTiles)) { 389 for (TileMap::iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) 390 it->value.layer->removeFromSuperlayer(); 391 } 392 393 auto boundsAtLastRevalidate = m_controller.boundsAtLastRevalidate(); 394 if (boundsAtLastRevalidate != bounds) { 395 // If there are margin tiles and the bounds have grown taller or wider, then the tiles that used to 396 // be bottom or right margin tiles need to be invalidated. 397 if (m_controller.hasMargins()) { 398 if (bounds.width() > boundsAtLastRevalidate.width() || bounds.height() > boundsAtLastRevalidate.height()) { 399 IntRect boundsWithoutMargin = m_controller.boundsWithoutMargin(); 400 IntRect oldBoundsWithoutMargin = m_controller.boundsAtLastRevalidateWithoutMargin(); 401 402 if (bounds.height() > boundsAtLastRevalidate.height()) { 403 IntRect formerBottomMarginRect = IntRect(oldBoundsWithoutMargin.x(), oldBoundsWithoutMargin.height(), 404 oldBoundsWithoutMargin.width(), boundsWithoutMargin.height() - oldBoundsWithoutMargin.height()); 405 setNeedsDisplayInRect(formerBottomMarginRect); 406 } 407 408 if (bounds.width() > boundsAtLastRevalidate.width()) { 409 IntRect formerRightMarginRect = IntRect(oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.y(), 410 boundsWithoutMargin.width() - oldBoundsWithoutMargin.width(), oldBoundsWithoutMargin.height()); 411 setNeedsDisplayInRect(formerRightMarginRect); 412 } 413 } 414 } 415 416 FloatRect scaledBounds(bounds); 417 scaledBounds.scale(m_scale); 418 IntRect boundsInTileCoords(enclosingIntRect(scaledBounds)); 419 420 TileIndex topLeftForBounds; 421 TileIndex bottomRightForBounds; 422 getTileIndexRangeForRect(boundsInTileCoords, topLeftForBounds, bottomRightForBounds); 423 424 Vector<TileIndex> tilesToRemove; 425 for (auto& index : m_tiles.keys()) { 426 if (index.y() < topLeftForBounds.y() || index.y() > bottomRightForBounds.y() || index.x() < topLeftForBounds.x() || index.x() > bottomRightForBounds.x()) 427 tilesToRemove.append(index); 428 } 429 removeTiles(tilesToRemove); 430 } 431 432 m_controller.didRevalidateTiles(); 433} 434 435TileGrid::TileCohort TileGrid::nextTileCohort() const 436{ 437 if (!m_cohortList.isEmpty()) 438 return m_cohortList.last().cohort + 1; 439 440 return 1; 441} 442 443void TileGrid::startedNewCohort(TileCohort cohort) 444{ 445 m_cohortList.append(TileCohortInfo(cohort, monotonicallyIncreasingTime())); 446#if PLATFORM(IOS) 447 if (!m_controller.isInWindow()) 448 tileControllerMemoryHandler().tileControllerGainedUnparentedTiles(&m_controller); 449#endif 450} 451 452TileGrid::TileCohort TileGrid::newestTileCohort() const 453{ 454 return m_cohortList.isEmpty() ? 0 : m_cohortList.last().cohort; 455} 456 457TileGrid::TileCohort TileGrid::oldestTileCohort() const 458{ 459 return m_cohortList.isEmpty() ? 0 : m_cohortList.first().cohort; 460} 461 462void TileGrid::scheduleCohortRemoval() 463{ 464 const double cohortRemovalTimerSeconds = 1; 465 466 // Start the timer, or reschedule the timer from now if it's already active. 467 if (!m_cohortRemovalTimer.isActive()) 468 m_cohortRemovalTimer.startRepeating(cohortRemovalTimerSeconds); 469} 470 471double TileGrid::TileCohortInfo::timeUntilExpiration() 472{ 473 double cohortLifeTimeSeconds = 2; 474 double timeThreshold = monotonicallyIncreasingTime() - cohortLifeTimeSeconds; 475 return creationTime - timeThreshold; 476} 477 478void TileGrid::cohortRemovalTimerFired(Timer<TileGrid>*) 479{ 480 if (m_cohortList.isEmpty()) { 481 m_cohortRemovalTimer.stop(); 482 return; 483 } 484 485 while (!m_cohortList.isEmpty() && m_cohortList.first().timeUntilExpiration() < 0) { 486 TileCohortInfo firstCohort = m_cohortList.takeFirst(); 487 removeTilesInCohort(firstCohort.cohort); 488 } 489 490 m_controller.updateTileCoverageMap(); 491} 492 493IntRect TileGrid::ensureTilesForRect(const FloatRect& rect, CoverageType newTileType) 494{ 495 if (m_controller.unparentsOffscreenTiles() && !m_controller.isInWindow()) 496 return IntRect(); 497 498 FloatRect scaledRect(rect); 499 scaledRect.scale(m_scale); 500 IntRect rectInTileCoords(enclosingIntRect(scaledRect)); 501 502 TileIndex topLeft; 503 TileIndex bottomRight; 504 getTileIndexRangeForRect(rectInTileCoords, topLeft, bottomRight); 505 506 TileCohort currCohort = nextTileCohort(); 507 unsigned tilesInCohort = 0; 508 509 IntRect coverageRect; 510 511 for (int y = topLeft.y(); y <= bottomRight.y(); ++y) { 512 for (int x = topLeft.x(); x <= bottomRight.x(); ++x) { 513 TileIndex tileIndex(x, y); 514 515 IntRect tileRect = rectForTileIndex(tileIndex); 516 TileInfo& tileInfo = m_tiles.add(tileIndex, TileInfo()).iterator->value; 517 518 coverageRect.unite(tileRect); 519 520 bool shouldChangeTileLayerFrame = false; 521 522 if (!tileInfo.layer) { 523 tileInfo.layer = m_controller.createTileLayer(tileRect, *this); 524 ASSERT(!m_tileRepaintCounts.contains(tileInfo.layer.get())); 525 } else { 526 // We already have a layer for this tile. Ensure that its size is correct. 527 FloatSize tileLayerSize(tileInfo.layer->bounds().size()); 528 shouldChangeTileLayerFrame = tileLayerSize != FloatSize(tileRect.size()); 529 530 if (shouldChangeTileLayerFrame) { 531 tileInfo.layer->setBounds(FloatRect(FloatPoint(), tileRect.size())); 532 tileInfo.layer->setPosition(tileRect.location()); 533 tileInfo.layer->setNeedsDisplay(); 534 } 535 } 536 537 if (newTileType == CoverageType::SecondaryTiles && !tileRect.intersects(m_primaryTileCoverageRect)) { 538 tileInfo.cohort = currCohort; 539 ++tilesInCohort; 540 } 541 542 bool shouldParentTileLayer = (!m_controller.unparentsOffscreenTiles() || m_controller.isInWindow()) && !tileInfo.layer->superlayer(); 543 544 if (shouldParentTileLayer) 545 m_containerLayer.get().appendSublayer(tileInfo.layer.get()); 546 } 547 } 548 549 if (tilesInCohort) 550 startedNewCohort(currCohort); 551 552 return coverageRect; 553} 554 555IntRect TileGrid::extent() const 556{ 557 TileIndex topLeft; 558 TileIndex bottomRight; 559 getTileIndexRangeForRect(m_primaryTileCoverageRect, topLeft, bottomRight); 560 561 // Return index of top, left tile and the number of tiles across and down. 562 return IntRect(topLeft.x(), topLeft.y(), bottomRight.x() - topLeft.x() + 1, bottomRight.y() - topLeft.y() + 1); 563} 564 565double TileGrid::retainedTileBackingStoreMemory() const 566{ 567 double totalBytes = 0; 568 569 for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { 570 const TileInfo& tileInfo = it->value; 571 if (tileInfo.layer->superlayer()) { 572 FloatRect bounds = tileInfo.layer->bounds(); 573 double contentsScale = tileInfo.layer->contentsScale(); 574 totalBytes += 4 * bounds.width() * contentsScale * bounds.height() * contentsScale; 575 } 576 } 577 578 return totalBytes; 579} 580 581// Return the rect in layer coords, not tile coords. 582IntRect TileGrid::tileCoverageRect() const 583{ 584 IntRect coverageRectInLayerCoords(m_primaryTileCoverageRect); 585 coverageRectInLayerCoords.scale(1 / m_scale); 586 return coverageRectInLayerCoords; 587} 588 589void TileGrid::drawTileMapContents(CGContextRef context, CGRect layerBounds) const 590{ 591 CGContextSetRGBFillColor(context, 0.3, 0.3, 0.3, 1); 592 CGContextFillRect(context, layerBounds); 593 594 CGFloat scaleFactor = layerBounds.size.width / m_controller.bounds().width(); 595 596 CGFloat contextScale = scaleFactor / m_scale; 597 CGContextScaleCTM(context, contextScale, contextScale); 598 599 for (TileMap::const_iterator it = m_tiles.begin(), end = m_tiles.end(); it != end; ++it) { 600 const TileInfo& tileInfo = it->value; 601 PlatformCALayer* tileLayer = tileInfo.layer.get(); 602 603 CGFloat red = 1; 604 CGFloat green = 1; 605 CGFloat blue = 1; 606 CGFloat alpha = 1; 607 if (tileInfo.hasStaleContent) { 608 red = 0.25; 609 green = 0.125; 610 blue = 0; 611 } else if (m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != VisibleTileCohort) { 612 red = 0.8; 613 green = 0.8; 614 blue = 0.8; 615 } 616 617 TileCohort newestCohort = newestTileCohort(); 618 TileCohort oldestCohort = oldestTileCohort(); 619 620 if (!m_controller.shouldAggressivelyRetainTiles() && tileInfo.cohort != VisibleTileCohort && newestCohort > oldestCohort) 621 alpha = 1 - (static_cast<float>((newestCohort - tileInfo.cohort)) / (newestCohort - oldestCohort)); 622 623 CGContextSetRGBFillColor(context, red, green, blue, alpha); 624 625 if (tileLayer->superlayer()) { 626 CGContextSetLineWidth(context, 0.5 / contextScale); 627 CGContextSetRGBStrokeColor(context, 0, 0, 0, 1); 628 } else { 629 CGContextSetLineWidth(context, 1 / contextScale); 630 CGContextSetRGBStrokeColor(context, 0.2, 0.1, 0.9, 1); 631 } 632 633 CGRect frame = CGRectMake(tileLayer->position().x(), tileLayer->position().y(), tileLayer->bounds().size().width(), tileLayer->bounds().size().height()); 634 CGContextFillRect(context, frame); 635 CGContextStrokeRect(context, frame); 636 } 637} 638 639void TileGrid::platformCALayerPaintContents(PlatformCALayer* platformCALayer, GraphicsContext& context, const FloatRect&) 640{ 641#if PLATFORM(IOS) 642 if (pthread_main_np()) 643 WebThreadLock(); 644#endif 645 646 { 647 GraphicsContextStateSaver stateSaver(context); 648 649 FloatPoint3D layerOrigin = platformCALayer->position(); 650 context.translate(-layerOrigin.x(), -layerOrigin.y()); 651 context.scale(FloatSize(m_scale, m_scale)); 652 653 PlatformCALayer::RepaintRectList dirtyRects = PlatformCALayer::collectRectsToPaint(context.platformContext(), platformCALayer); 654 PlatformCALayer::drawLayerContents(context.platformContext(), &m_controller.rootLayer(), dirtyRects); 655 } 656 657 int repaintCount = platformCALayerIncrementRepaintCount(platformCALayer); 658 if (m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0)) 659 PlatformCALayer::drawRepaintIndicator(context.platformContext(), platformCALayer, repaintCount, cachedCGColor(m_controller.tileDebugBorderColor(), ColorSpaceDeviceRGB)); 660 661 if (m_controller.scrollingPerformanceLoggingEnabled()) { 662 FloatRect visiblePart(platformCALayer->position().x(), platformCALayer->position().y(), platformCALayer->bounds().size().width(), platformCALayer->bounds().size().height()); 663 visiblePart.intersect(m_controller.visibleRect()); 664 665 if (repaintCount == 1 && !visiblePart.isEmpty()) 666 WTFLogAlways("SCROLLING: Filled visible fresh tile. Time: %f Unfilled Pixels: %u\n", WTF::monotonicallyIncreasingTime(), blankPixelCount()); 667 } 668} 669 670float TileGrid::platformCALayerDeviceScaleFactor() const 671{ 672 return m_controller.rootLayer().owner()->platformCALayerDeviceScaleFactor(); 673} 674 675bool TileGrid::platformCALayerShowDebugBorders() const 676{ 677 return m_controller.rootLayer().owner()->platformCALayerShowDebugBorders(); 678} 679 680bool TileGrid::platformCALayerShowRepaintCounter(PlatformCALayer*) const 681{ 682 return m_controller.rootLayer().owner()->platformCALayerShowRepaintCounter(0); 683} 684 685bool TileGrid::platformCALayerContentsOpaque() const 686{ 687 return m_controller.tilesAreOpaque(); 688} 689 690int TileGrid::platformCALayerIncrementRepaintCount(PlatformCALayer* platformCALayer) 691{ 692 int repaintCount = 0; 693 694 if (m_tileRepaintCounts.contains(platformCALayer)) 695 repaintCount = m_tileRepaintCounts.get(platformCALayer); 696 697 m_tileRepaintCounts.set(platformCALayer, ++repaintCount); 698 699 return repaintCount; 700} 701 702#if PLATFORM(IOS) 703void TileGrid::removeUnparentedTilesNow() 704{ 705 while (!m_cohortList.isEmpty()) { 706 TileCohortInfo firstCohort = m_cohortList.takeFirst(); 707 removeTilesInCohort(firstCohort.cohort); 708 } 709} 710#endif 711 712} // namespace WebCore 713